26
26
import awscrt .exceptions
27
27
from typing_extensions import NoReturn
28
28
29
- from otaclient_iot_logging_server ._common import LogEvent , LogMessage , LogsQueue
29
+ from otaclient_iot_logging_server ._common import (
30
+ LogEvent ,
31
+ LogGroupType ,
32
+ LogMessage ,
33
+ LogsQueue ,
34
+ )
30
35
from otaclient_iot_logging_server ._utils import retry
31
36
from otaclient_iot_logging_server .boto3_session import get_session
32
37
from otaclient_iot_logging_server .configs import server_cfg
@@ -68,40 +73,45 @@ def __init__(
68
73
69
74
self ._session_config = session_config
70
75
self ._log_group_name = session_config .aws_cloudwatch_log_group
76
+ self ._metrics_group_name = session_config .aws_cloudwatch_metrics_log_group
71
77
self ._interval = interval
72
78
self ._queue : LogsQueue = queue
73
79
# NOTE: add this limitation to ensure all of the log_streams in a merge
74
80
# will definitely have entries less than MAX_LOGS_PER_PUT
75
81
self ._max_logs_per_merge = min (max_logs_per_merge , self .MAX_LOGS_PER_PUT )
76
82
77
83
@retry (max_retry = 16 , backoff_factor = 2 , backoff_max = 32 )
78
- def _create_log_group (self ):
84
+ def _create_log_groups (self ):
79
85
# TODO: (20240214) should we let the edge side iot_logging_server
80
86
# create the log group?
81
- log_group_name , client = self ._log_group_name , self ._client
87
+ log_group_names = [self ._log_group_name , self ._metrics_group_name ]
88
+ client = self ._client
82
89
exc_types = self ._exc_types
83
- try :
84
- client .create_log_group (logGroupName = log_group_name )
85
- logger .info (f"{ log_group_name = } has been created" )
86
- except exc_types .ResourceAlreadyExistsException as e :
87
- logger .debug (
88
- f"{ log_group_name = } already existed, skip creating: { e .response } "
89
- )
90
- except ValueError as e :
91
- if e .__cause__ and isinstance (e .__cause__ , awscrt .exceptions .AwsCrtError ):
92
- logger .error (
93
- (f"failed to create mtls connection to remote: { e .__cause__ } " )
90
+ for log_group_name in log_group_names :
91
+ try :
92
+ client .create_log_group (logGroupName = log_group_name )
93
+ logger .info (f"{ log_group_name = } has been created" )
94
+ except exc_types .ResourceAlreadyExistsException as e :
95
+ logger .debug (
96
+ f"{ log_group_name = } already existed, skip creating: { e .response } "
94
97
)
95
- raise e .__cause__ from None
96
- logger .error (f"failed to create { log_group_name = } : { e !r} " )
97
- raise
98
- except Exception as e :
99
- logger .error (f"failed to create { log_group_name = } : { e !r} " )
100
- raise
98
+ except ValueError as e :
99
+ if e .__cause__ and isinstance (
100
+ e .__cause__ , awscrt .exceptions .AwsCrtError
101
+ ):
102
+ logger .error (
103
+ (f"failed to create mtls connection to remote: { e .__cause__ } " )
104
+ )
105
+ raise e .__cause__ from None
106
+ logger .error (f"failed to create { log_group_name = } : { e !r} " )
107
+ raise
108
+ except Exception as e :
109
+ logger .error (f"failed to create { log_group_name = } : { e !r} " )
110
+ raise
101
111
102
112
@retry (max_retry = 16 , backoff_factor = 2 , backoff_max = 32 )
103
- def _create_log_stream (self , log_stream_name : str ):
104
- log_group_name , client = self . _log_group_name , self ._client
113
+ def _create_log_stream (self , log_group_name : str , log_stream_name : str ):
114
+ client = self ._client
105
115
exc_types = self ._exc_types
106
116
try :
107
117
client .create_log_stream (
@@ -126,7 +136,9 @@ def _create_log_stream(self, log_stream_name: str):
126
136
raise
127
137
128
138
@retry (backoff_factor = 2 )
129
- def put_log_events (self , log_stream_name : str , message_list : list [LogMessage ]):
139
+ def put_log_events (
140
+ self , log_group_name : str , log_stream_name : str , message_list : list [LogMessage ]
141
+ ):
130
142
"""
131
143
Ref:
132
144
https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/logs/client/put_log_events.html
@@ -137,7 +149,7 @@ def put_log_events(self, log_stream_name: str, message_list: list[LogMessage]):
137
149
See the documentation for more details.
138
150
"""
139
151
request = LogEvent (
140
- logGroupName = self . _log_group_name ,
152
+ logGroupName = log_group_name ,
141
153
logStreamName = log_stream_name ,
142
154
logEvents = message_list ,
143
155
)
@@ -148,44 +160,54 @@ def put_log_events(self, log_stream_name: str, message_list: list[LogMessage]):
148
160
# logger.debug(f"successfully uploaded: {response}")
149
161
except exc_types .ResourceNotFoundException as e :
150
162
logger .debug (f"{ log_stream_name = } not found: { e !r} " )
151
- self ._create_log_stream (log_stream_name )
163
+ self ._create_log_stream (log_group_name , log_stream_name )
152
164
raise
153
165
except Exception as e :
154
166
# NOTE: for unhandled exception, we just log it and ignore,
155
167
# leave for the developer to properly handle it
156
168
# in the future!
157
169
logger .error (
158
170
f"put_log_events failure: { e !r} \n "
159
- f"log_group_name={ self . _log_group_name } , \n "
171
+ f"log_group_name={ log_group_name } , \n "
160
172
f"log_stream_name={ log_stream_name } "
161
173
)
162
174
163
175
def thread_main (self ) -> NoReturn :
164
176
"""Main entry for running this iot_logger in a thread."""
165
177
# unconditionally create log_group and log_stream, do nothing if existed.
166
- self ._create_log_group ()
178
+ self ._create_log_groups ()
167
179
168
180
while True :
169
181
# merge LogMessages into the same source, identified by
170
- # log_stream_suffix.
171
- message_dict : dict [str , list [LogMessage ]] = defaultdict (list )
182
+ # log_group_type and log_stream_suffix.
183
+ message_dict : dict [
184
+ (log_group_type , log_stream_suffix ), list [LogMessage ]
185
+ ] = defaultdict (list )
172
186
173
187
_merge_count = 0
174
188
while _merge_count < self ._max_logs_per_merge :
175
189
_queue = self ._queue
176
190
try :
177
- log_stream_suffix , message = _queue .get_nowait ()
191
+ log_group_type , log_stream_suffix , message = _queue .get_nowait ()
178
192
_merge_count += 1
179
-
180
- message_dict [log_stream_suffix ].append (message )
193
+ message_dict [(log_group_type , log_stream_suffix )].append (message )
181
194
except Empty :
182
195
break
183
196
184
- for log_stream_suffix , logs in message_dict .items ():
197
+ for (log_group_type , log_stream_suffix ), logs in message_dict .items ():
198
+ # get the log_group_name based on the log_group_type
199
+ log_group_name = (
200
+ self ._metrics_group_name
201
+ if log_group_type == LogGroupType .METRICS
202
+ else self ._log_group_name
203
+ )
204
+
185
205
with contextlib .suppress (Exception ):
186
206
self .put_log_events (
207
+ log_group_name ,
187
208
get_log_stream_name (
188
- self ._session_config .thing_name , log_stream_suffix
209
+ self ._session_config .thing_name ,
210
+ log_stream_suffix ,
189
211
),
190
212
logs ,
191
213
)
0 commit comments