|
@@ -1,9 +1,12 @@
|
|
|
import asyncio
|
|
|
+import json
|
|
|
+import logging
|
|
|
import os
|
|
|
import secrets
|
|
|
from collections.abc import AsyncIterable as AsyncIterableABC
|
|
|
from contextlib import closing, suppress
|
|
|
from dataclasses import dataclass
|
|
|
+from datetime import datetime
|
|
|
from importlib.resources import path
|
|
|
from typing import Any, AsyncIterator, Awaitable, Callable, Dict, List, Optional, Sequence, Tuple, Type, TypeVar, Union
|
|
|
|
|
@@ -16,7 +19,7 @@ from hivemind.p2p.p2p_daemon_bindings.control import P2PDaemonError, P2PHandlerE
|
|
|
from hivemind.p2p.p2p_daemon_bindings.datastructures import PeerID, PeerInfo, StreamInfo
|
|
|
from hivemind.proto.p2pd_pb2 import RPCError
|
|
|
from hivemind.utils.asyncio import as_aiter, asingle
|
|
|
-from hivemind.utils.logging import get_logger
|
|
|
+from hivemind.utils.logging import get_logger, golog_level_to_python, loglevel, python_level_to_golog
|
|
|
|
|
|
logger = get_logger(__name__)
|
|
|
|
|
@@ -168,8 +171,13 @@ class P2P:
|
|
|
**process_kwargs,
|
|
|
)
|
|
|
|
|
|
+ env = os.environ.copy()
|
|
|
+ env.setdefault("GOLOG_LOG_LEVEL", python_level_to_golog(loglevel))
|
|
|
+ env["GOLOG_LOG_FMT"] = "json"
|
|
|
+
|
|
|
+ logger.debug(f"Launching {proc_args}")
|
|
|
self._child = await asyncio.subprocess.create_subprocess_exec(
|
|
|
- *proc_args, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.STDOUT
|
|
|
+ *proc_args, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.STDOUT, env=env
|
|
|
)
|
|
|
self._alive = True
|
|
|
|
|
@@ -560,8 +568,44 @@ class P2P:
|
|
|
break
|
|
|
last_line = line.rstrip().decode(errors="ignore")
|
|
|
|
|
|
+ self._log_p2pd_message(last_line)
|
|
|
if last_line.startswith("Peer ID:"):
|
|
|
ready.set_result(None)
|
|
|
|
|
|
if not ready.done():
|
|
|
ready.set_exception(P2PDaemonError(f"Daemon failed to start: {last_line}"))
|
|
|
+
|
|
|
+ @staticmethod
|
|
|
+ def _log_p2pd_message(line: str) -> None:
|
|
|
+ if '"logger"' not in line: # User-friendly info from p2pd stdout
|
|
|
+ logger.debug(line, extra={"caller": "p2pd"})
|
|
|
+ return
|
|
|
+
|
|
|
+ try:
|
|
|
+ record = json.loads(line)
|
|
|
+ caller = record["caller"]
|
|
|
+
|
|
|
+ level = golog_level_to_python(record["level"])
|
|
|
+ if level <= logging.WARNING:
|
|
|
+ # Many Go loggers are excessively verbose (e.g. show warnings for unreachable peers),
|
|
|
+ # so we downgrade INFO and WARNING messages to DEBUG.
|
|
|
+ # The Go verbosity can still be controlled via the GOLOG_LOG_LEVEL env variable.
|
|
|
+ # Details: https://github.com/ipfs/go-log#golog_log_level
|
|
|
+ level = logging.DEBUG
|
|
|
+
|
|
|
+ message = record["msg"]
|
|
|
+ if "error" in record:
|
|
|
+ message += f": {record['error']}"
|
|
|
+
|
|
|
+ logger.log(
|
|
|
+ level,
|
|
|
+ message,
|
|
|
+ extra={
|
|
|
+ "origin_created": datetime.strptime(record["ts"], "%Y-%m-%dT%H:%M:%S.%f%z").timestamp(),
|
|
|
+ "caller": caller,
|
|
|
+ },
|
|
|
+ )
|
|
|
+ except Exception:
|
|
|
+ # Parsing errors are unlikely, but we don't want to lose these messages anyway
|
|
|
+ logger.warning(line, extra={"caller": "p2pd"})
|
|
|
+ logger.exception("Failed to parse go-log message:")
|