Duibonduil commited on
Commit
31e8fad
·
verified ·
1 Parent(s): 0d77048

Upload 3 files

Browse files
aworld/logs/__init__.py ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ # coding: utf-8
2
+ # Copyright (c) 2025 inclusionAI.
aworld/logs/log.py ADDED
@@ -0,0 +1,174 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import time
2
+ import sys
3
+ import os
4
+ from logging import StreamHandler
5
+ from abc import ABC
6
+ from logging import Logger, NOTSET, LogRecord, Filter, Formatter, Handler
7
+ from typing import Optional, Union
8
+
9
+ from aworld.trace.base import get_tracer_provider_silent, Tracer, AttributeValueType
10
+
11
+ TRACE_LOG_FORMAT = '%(asctime)s - [%(trace_id)s] - [%(span_id)s] - %(name)s - %(levelname)s - %(message)s'
12
+ SPECIAL_TRACE_LOG_FORMAT = '%(asctime)s - [trace_%(trace_id)s] - [%(span_id)s] - %(name)s - %(levelname)s - %(message)s'
13
+
14
+
15
+ class LoggerProvider(ABC):
16
+ """A logger provider is a factory for loggers."""
17
+
18
+
19
+ _GLOBAL_LOG_PROVIDER: Optional[LoggerProvider] = None
20
+
21
+
22
+ def set_log_provider(provider: str = "otlp",
23
+ backend: str = "logfire",
24
+ base_url: str = None,
25
+ write_token: str = None,
26
+ **kwargs):
27
+ """Set the global log provider."""
28
+
29
+ global _GLOBAL_LOG_PROVIDER
30
+
31
+ if provider == "otlp":
32
+ from .opentelemetry.otlp_log import OTLPLoggerProvider
33
+ _GLOBAL_LOG_PROVIDER = OTLPLoggerProvider(backend=backend,
34
+ base_url=base_url,
35
+ write_token=write_token,
36
+ **kwargs)
37
+
38
+
39
+ def get_log_provider() -> LoggerProvider:
40
+ """
41
+ Get the global log provider.
42
+ """
43
+ global _GLOBAL_LOG_PROVIDER
44
+ if _GLOBAL_LOG_PROVIDER is None:
45
+ raise ValueError("No log provider has been set.")
46
+ return _GLOBAL_LOG_PROVIDER
47
+
48
+
49
+ def instrument_logging(logger: Logger, level: Union[int, str] = NOTSET) -> None:
50
+ """Instrument the logger."""
51
+ for handler in logger.root.handlers:
52
+ if not any(isinstance(filter, TraceLoggingFilter) for filter in handler.filters):
53
+ handler.setFormatter(Formatter(TRACE_LOG_FORMAT))
54
+ handler.addFilter(TraceLoggingFilter())
55
+
56
+ if not logger.handlers:
57
+ print("No handlers found, adding a StreamHandler. logger=", logger.name)
58
+ handler = StreamHandler()
59
+ handler.setFormatter(Formatter(SPECIAL_TRACE_LOG_FORMAT))
60
+ handler.addFilter(TraceLoggingFilter())
61
+ logger.addHandler(handler)
62
+ else:
63
+ for handler in logger.handlers:
64
+ if not any(isinstance(filter, TraceLoggingFilter) for filter in handler.filters):
65
+ handler.setFormatter(Formatter(SPECIAL_TRACE_LOG_FORMAT))
66
+ handler.addFilter(TraceLoggingFilter())
67
+ logger.propagate = False
68
+ logger.addHandler(TraceLogginHandler(level))
69
+
70
+
71
+ class TraceLoggingFilter(Filter):
72
+ """
73
+ A filter that adds trace information to log records.
74
+ """
75
+
76
+ def filter(self, record: LogRecord) -> bool:
77
+ """
78
+ Add trace information to the log record.
79
+ """
80
+ trace = get_tracer_provider_silent()
81
+ if trace:
82
+ span = trace.get_current_span()
83
+ record.trace_id = span.get_trace_id() if span else None
84
+ record.span_id = span.get_span_id() if span else None
85
+ return True
86
+
87
+
88
+ class TraceLogginHandler(Handler):
89
+ """
90
+ A handler class which writes logging records, appropriately formatted,
91
+ to a stream. Note that this class does not close the stream, as
92
+ sys.stdout or sys.stderr may be used.
93
+ """
94
+ @staticmethod
95
+ def strip_color(text: str) -> str:
96
+ """Remove ANSI color codes from text"""
97
+ import re
98
+ return re.sub(r'\033\[[0-9;]*m', '', text)
99
+
100
+ def __init__(self,
101
+ level: Union[int, str] = NOTSET,
102
+ tracer_name: str = "aworld.log") -> None:
103
+ """Initialize the handler."""
104
+ super().__init__(level=level)
105
+ self._tracer_name = tracer_name
106
+ self._tracer: Tracer = None
107
+
108
+ def emit(self, record: LogRecord) -> None:
109
+ """Emit a record."""
110
+ trace = get_tracer_provider_silent()
111
+ if not trace or not trace.get_current_span() or not trace.get_current_span().is_recording():
112
+ return
113
+
114
+ if not self._tracer:
115
+ self._tracer = trace.get_tracer(name=self._tracer_name)
116
+
117
+ try:
118
+ f = sys._getframe()
119
+ while f:
120
+ if 'logging/__init__.py' in f.f_code.co_filename or \
121
+ f.f_code.co_filename.startswith(os.path.dirname(__file__)):
122
+ f = f.f_back
123
+ else:
124
+ break
125
+
126
+ origin_msg = record.msg
127
+ raw_msg = None
128
+ if f:
129
+ try:
130
+ import linecache
131
+ line = linecache.getline(f.f_code.co_filename, f.f_lineno)
132
+ if 'logger.' in line:
133
+ raw_msg = line.split('logger.', 1)[1].split(
134
+ '(', 1)[1].split(')', 1)[0].strip()
135
+ except:
136
+ pass
137
+ record.msg = self.strip_color(record.msg)
138
+ msg_template = raw_msg if raw_msg else record.msg
139
+
140
+ if len(msg_template) > 255:
141
+ msg_template = msg_template[:255] + '...'
142
+
143
+ attributes = {
144
+ 'code.filepath': f.f_code.co_filename if f else record.pathname,
145
+ 'code.lineno': f.f_lineno if f else record.lineno,
146
+ 'code.function': f.f_code.co_name if f else record.funcName,
147
+ 'log.level': record.levelname,
148
+ 'log.logger': record.name,
149
+ 'log.message': self.format(record),
150
+ }
151
+ record.msg = origin_msg
152
+ self._create_span(
153
+ span_name=msg_template,
154
+ attributes=attributes,
155
+ exc_info=record.exc_info,
156
+ )
157
+ except RecursionError: # See issue 36272
158
+ raise
159
+ except Exception:
160
+ self.handleError(record)
161
+
162
+ def _create_span(self,
163
+ span_name: str,
164
+ attributes: dict[str, AttributeValueType] = None,
165
+ exc_info: BaseException = None):
166
+ start_time = time.time_ns()
167
+ span = self._tracer.start_span(
168
+ name=span_name,
169
+ attributes=attributes,
170
+ start_time=start_time,
171
+ )
172
+ if exc_info:
173
+ span.record_exception(exception=exc_info, timestamp=start_time)
174
+ span.end()
aworld/logs/util.py ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # coding: utf-8
2
+ # Copyright (c) 2025 inclusionAI.
3
+
4
+ import logging
5
+
6
+ LOG_FORMAT = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
7
+ logging.basicConfig(level=logging.INFO, format=LOG_FORMAT)
8
+ # common logger info
9
+ logger = logging.getLogger("common")
10
+ # for trace info
11
+ trace_logger = logging.getLogger("traced")
12
+
13
+
14
+ class Color:
15
+ black = '\033[30m'
16
+ red = '\033[31m'
17
+ green = '\033[32m'
18
+ orange = '\033[33m'
19
+ blue = '\033[34m'
20
+ purple = '\033[35m'
21
+ cyan = '\033[36m'
22
+ lightgrey = '\033[37m'
23
+ darkgrey = '\033[90m'
24
+ lightred = '\033[91m'
25
+ lightgreen = '\033[92m'
26
+ yellow = '\033[93m'
27
+ lightblue = '\033[94m'
28
+ pink = '\033[95m'
29
+ lightcyan = '\033[96m'
30
+ reset = '\033[0m'
31
+ bold = '\033[01m'
32
+ disable = '\033[02m'
33
+ underline = '\033[04m'
34
+ reverse = '\033[07m'
35
+ strikethrough = '\033[09m'
36
+
37
+
38
+ def color_log(value, color: str = Color.black, logger_: logging.Logger = logger, level: int = logging.INFO, hightlight_key=None):
39
+ """ Colored value or highlight key in log.
40
+
41
+ Args:
42
+ value:
43
+ color: Color
44
+ hightlight_key: Color segment key.
45
+ """
46
+ if hightlight_key is None:
47
+ logger_.log(level, f"{color} {value} {Color.reset}")
48
+ else:
49
+ logger_.log(level, f"{color} {hightlight_key}: {Color.reset} {value}")
50
+
51
+
52
+ def aworld_log(logger, color: str = Color.black, level: int = logging.INFO):
53
+ """Colored log style in the Aworld.
54
+
55
+ Args:
56
+ color: Default color set, different types of information can be set in different colors.
57
+ level: Log level.
58
+ """
59
+ def_color = color
60
+
61
+ def decorator(value, color: str = None):
62
+ # Set color in the called.
63
+ if color:
64
+ color_log(value, color, logger, level)
65
+ else:
66
+ color_log(value, def_color, logger, level)
67
+
68
+ return decorator
69
+
70
+
71
+ def init_logger(logger: logging.Logger):
72
+ logger.debug = aworld_log(logger, color=Color.lightgrey, level=logging.DEBUG)
73
+ logger.info = aworld_log(logger, color=Color.black, level=logging.INFO)
74
+ logger.warning = aworld_log(logger, color=Color.orange, level=logging.WARNING)
75
+ logger.warn = logger.warning
76
+ logger.error = aworld_log(logger, color=Color.red, level=logging.ERROR)
77
+ logger.fatal = logger.error
78
+
79
+
80
+ init_logger(logger)
81
+ init_logger(trace_logger)