11from __future__ import annotations
22import json
3- from datetime import timedelta
43from enum import Enum
5- from typing import Optional , Any , Dict , TYPE_CHECKING
4+ from typing import List , Optional , Any , Dict , TYPE_CHECKING
65
76import requests
87
98from ravendb import ServerNode
109from ravendb .http .raven_command import RavenCommand , VoidRavenCommand
1110from ravendb .serverwide .operations .common import ServerOperation , T , VoidServerOperation
12- from ravendb .tools .utils import Utils
1311
1412if TYPE_CHECKING :
1513 from ravendb .documents .conventions import DocumentConventions
1614
1715
18- class LogMode (Enum ):
19- NONE = "None"
20- OPERATIONS = "Operations"
21- INFORMATION = "Information"
16+ class LogLevel (Enum ):
17+ # RavenDB 7.x logging is NLog-based; the old LogMode (None/Operations/Information) is gone.
18+ TRACE = "Trace"
19+ DEBUG = "Debug"
20+ INFO = "Info"
21+ WARN = "Warn"
22+ ERROR = "Error"
23+ FATAL = "Fatal"
24+ OFF = "Off"
2225
26+ def __str__ (self ):
27+ return self .value
2328
24- class GetLogsConfigurationResult :
29+
30+ class LogFilterAction (Enum ):
31+ NEUTRAL = "Neutral"
32+ LOG = "Log"
33+ IGNORE = "Ignore"
34+ LOG_FINAL = "LogFinal"
35+ IGNORE_FINAL = "IgnoreFinal"
36+
37+ def __str__ (self ):
38+ return self .value
39+
40+
41+ class LogFilter :
42+ def __init__ (
43+ self ,
44+ min_level : LogLevel = None ,
45+ max_level : LogLevel = None ,
46+ condition : str = None ,
47+ action : LogFilterAction = None ,
48+ ):
49+ self .min_level = min_level
50+ self .max_level = max_level
51+ self .condition = condition
52+ self .action = action
53+
54+ @classmethod
55+ def from_json (cls , json_dict : Dict [str , Any ]) -> LogFilter :
56+ return cls (
57+ LogLevel (json_dict ["MinLevel" ]),
58+ LogLevel (json_dict ["MaxLevel" ]),
59+ json_dict .get ("Condition" ),
60+ LogFilterAction (json_dict ["Action" ]),
61+ )
62+
63+ def to_json (self ) -> Dict [str , Any ]:
64+ return {
65+ "MinLevel" : self .min_level .value ,
66+ "MaxLevel" : self .max_level .value ,
67+ "Condition" : self .condition ,
68+ "Action" : self .action .value ,
69+ }
70+
71+
72+ # ---- GET /admin/logs/configuration result sub-objects ----
73+ class LogsConfiguration :
74+ def __init__ (
75+ self ,
76+ path : str = None ,
77+ current_min_level : LogLevel = None ,
78+ current_filters : List [LogFilter ] = None ,
79+ current_log_filter_default_action : LogFilterAction = None ,
80+ min_level : LogLevel = None ,
81+ archive_above_size_in_mb : int = None ,
82+ max_archive_days : int = None ,
83+ max_archive_files : int = None ,
84+ enable_archive_file_compression : bool = None ,
85+ ):
86+ self .path = path
87+ self .current_min_level = current_min_level
88+ self .current_filters = current_filters if current_filters is not None else []
89+ self .current_log_filter_default_action = current_log_filter_default_action
90+ self .min_level = min_level
91+ self .archive_above_size_in_mb = archive_above_size_in_mb
92+ self .max_archive_days = max_archive_days
93+ self .max_archive_files = max_archive_files
94+ self .enable_archive_file_compression = enable_archive_file_compression
95+
96+ @classmethod
97+ def from_json (cls , json_dict : Dict [str , Any ]) -> LogsConfiguration :
98+ return cls (
99+ path = json_dict .get ("Path" ),
100+ current_min_level = LogLevel (json_dict ["CurrentMinLevel" ]),
101+ current_filters = [LogFilter .from_json (f ) for f in (json_dict .get ("CurrentFilters" ) or [])],
102+ current_log_filter_default_action = LogFilterAction (json_dict ["CurrentLogFilterDefaultAction" ]),
103+ min_level = LogLevel (json_dict ["MinLevel" ]),
104+ archive_above_size_in_mb = json_dict .get ("ArchiveAboveSizeInMb" ),
105+ max_archive_days = json_dict .get ("MaxArchiveDays" ),
106+ max_archive_files = json_dict .get ("MaxArchiveFiles" ),
107+ enable_archive_file_compression = json_dict .get ("EnableArchiveFileCompression" ),
108+ )
109+
110+
111+ class AuditLogsConfiguration :
25112 def __init__ (
26113 self ,
27- current_mode : LogMode = None ,
28- mode : LogMode = None ,
29114 path : str = None ,
30- use_utc_time : bool = None ,
31- retention_time : timedelta = None ,
32- retention_size : int = None ,
33- compress : bool = None ,
115+ level : LogLevel = None ,
116+ archive_above_size_in_mb : int = None ,
117+ max_archive_days : int = None ,
118+ max_archive_files : int = None ,
119+ enable_archive_file_compression : bool = None ,
34120 ):
35- self .current_mode = current_mode
36- self .mode = mode
37121 self .path = path
38- self .use_utc_time = use_utc_time
39- self .retention_time = retention_time
40- self .retention_size = retention_size
41- self .compress = compress
122+ self .level = level
123+ self .archive_above_size_in_mb = archive_above_size_in_mb
124+ self .max_archive_days = max_archive_days
125+ self .max_archive_files = max_archive_files
126+ self .enable_archive_file_compression = enable_archive_file_compression
127+
128+ @classmethod
129+ def from_json (cls , json_dict : Dict [str , Any ]) -> AuditLogsConfiguration :
130+ return cls (
131+ path = json_dict .get ("Path" ),
132+ level = LogLevel (json_dict ["Level" ]),
133+ archive_above_size_in_mb = json_dict .get ("ArchiveAboveSizeInMb" ),
134+ max_archive_days = json_dict .get ("MaxArchiveDays" ),
135+ max_archive_files = json_dict .get ("MaxArchiveFiles" ),
136+ enable_archive_file_compression = json_dict .get ("EnableArchiveFileCompression" ),
137+ )
138+
139+
140+ class MicrosoftLogsConfiguration :
141+ def __init__ (self , current_min_level : LogLevel = None , min_level : LogLevel = None ):
142+ self .current_min_level = current_min_level
143+ self .min_level = min_level
144+
145+ @classmethod
146+ def from_json (cls , json_dict : Dict [str , Any ]) -> MicrosoftLogsConfiguration :
147+ return cls (LogLevel (json_dict ["CurrentMinLevel" ]), LogLevel (json_dict ["MinLevel" ]))
148+
149+
150+ class AdminLogsConfiguration :
151+ def __init__ (
152+ self ,
153+ current_min_level : LogLevel = None ,
154+ current_filters : List [LogFilter ] = None ,
155+ current_log_filter_default_action : LogFilterAction = None ,
156+ ):
157+ self .current_min_level = current_min_level
158+ self .current_filters = current_filters if current_filters is not None else []
159+ self .current_log_filter_default_action = current_log_filter_default_action
160+
161+ @classmethod
162+ def from_json (cls , json_dict : Dict [str , Any ]) -> AdminLogsConfiguration :
163+ return cls (
164+ LogLevel (json_dict ["CurrentMinLevel" ]),
165+ [LogFilter .from_json (f ) for f in (json_dict .get ("CurrentFilters" ) or [])],
166+ LogFilterAction (json_dict ["CurrentLogFilterDefaultAction" ]),
167+ )
168+
169+
170+ class GetLogsConfigurationResult :
171+ def __init__ (
172+ self ,
173+ logs : LogsConfiguration = None ,
174+ audit_logs : AuditLogsConfiguration = None ,
175+ microsoft_logs : MicrosoftLogsConfiguration = None ,
176+ admin_logs : AdminLogsConfiguration = None ,
177+ ):
178+ self .logs = logs
179+ self .audit_logs = audit_logs
180+ self .microsoft_logs = microsoft_logs
181+ self .admin_logs = admin_logs
42182
43183 @classmethod
44184 def from_json (cls , json_dict : Dict [str , Any ]) -> GetLogsConfigurationResult :
45185 return cls (
46- LogMode (json_dict ["CurrentMode" ]),
47- LogMode (json_dict ["Mode" ]),
48- json_dict ["Path" ],
49- json_dict ["UseUtcTime" ],
50- Utils .string_to_timedelta (json_dict ["RetentionTime" ]),
51- json_dict ["RetentionSize" ],
52- json_dict ["Compress" ],
186+ LogsConfiguration .from_json (json_dict ["Logs" ]) if json_dict .get ("Logs" ) else None ,
187+ AuditLogsConfiguration .from_json (json_dict ["AuditLogs" ]) if json_dict .get ("AuditLogs" ) else None ,
188+ (
189+ MicrosoftLogsConfiguration .from_json (json_dict ["MicrosoftLogs" ])
190+ if json_dict .get ("MicrosoftLogs" )
191+ else None
192+ ),
193+ AdminLogsConfiguration .from_json (json_dict ["AdminLogs" ]) if json_dict .get ("AdminLogs" ) else None ,
53194 )
54195
55196
@@ -62,7 +203,7 @@ def __init__(self):
62203 super ().__init__ (GetLogsConfigurationResult )
63204
64205 def is_read_request (self ) -> bool :
65- return False
206+ return True
66207
67208 def create_request (self , node : ServerNode ) -> requests .Request :
68209 return requests .Request ("GET" , f"{ node .url } /admin/logs/configuration" )
@@ -75,34 +216,84 @@ def set_response(self, response: Optional[str], from_cache: bool) -> None:
75216
76217
77218class SetLogsConfigurationOperation (VoidServerOperation ):
219+ # Mirrors the .NET client: one call sets exactly one of Logs / MicrosoftLogs / AdminLogs (+ Persist).
220+ class LogsConfiguration :
221+ def __init__ (
222+ self ,
223+ min_level : LogLevel ,
224+ filters : List [LogFilter ] = None ,
225+ log_filter_default_action : LogFilterAction = None ,
226+ ):
227+ self .min_level = min_level
228+ self .filters = filters if filters is not None else []
229+ self .log_filter_default_action = log_filter_default_action
230+
231+ def to_json (self ) -> Dict [str , Any ]:
232+ result = {"MinLevel" : self .min_level .value , "Filters" : [f .to_json () for f in self .filters ]}
233+ if self .log_filter_default_action is not None :
234+ result ["LogFilterDefaultAction" ] = self .log_filter_default_action .value
235+ return result
236+
237+ class MicrosoftLogsConfiguration :
238+ def __init__ (self , min_level : LogLevel ):
239+ self .min_level = min_level
240+
241+ def to_json (self ) -> Dict [str , Any ]:
242+ return {"MinLevel" : self .min_level .value }
243+
244+ class AdminLogsConfiguration :
245+ def __init__ (
246+ self ,
247+ min_level : LogLevel ,
248+ filters : List [LogFilter ] = None ,
249+ log_filter_default_action : LogFilterAction = None ,
250+ ):
251+ self .min_level = min_level
252+ self .filters = filters if filters is not None else []
253+ self .log_filter_default_action = log_filter_default_action
254+
255+ def to_json (self ) -> Dict [str , Any ]:
256+ result = {"MinLevel" : self .min_level .value , "Filters" : [f .to_json () for f in self .filters ]}
257+ if self .log_filter_default_action is not None :
258+ result ["LogFilterDefaultAction" ] = self .log_filter_default_action .value
259+ return result
260+
78261 class Parameters :
79262 def __init__ (
80263 self ,
81- mode : LogMode = None ,
82- retention_time : timedelta = None ,
83- retention_size : int = None ,
84- compress : bool = None ,
264+ logs : "SetLogsConfigurationOperation.LogsConfiguration" = None ,
265+ microsoft_logs : "SetLogsConfigurationOperation.MicrosoftLogsConfiguration" = None ,
266+ admin_logs : "SetLogsConfigurationOperation.AdminLogsConfiguration" = None ,
267+ persist : bool = False ,
85268 ) -> None :
86- self .mode = mode
87- self .retention_time = retention_time
88- self .retention_size = retention_size
89- self .compress = compress
90-
91- @classmethod
92- def from_get_logs (cls , get_logs : GetLogsConfigurationResult ) -> "SetLogsConfigurationOperation.Parameters" :
93- return cls (get_logs .mode , get_logs .retention_time , get_logs .retention_size , get_logs .compress )
269+ self .logs = logs
270+ self .microsoft_logs = microsoft_logs
271+ self .admin_logs = admin_logs
272+ self .persist = persist
94273
95274 def to_json (self ) -> Dict [str , Any ]:
96- return {
97- "Mode" : self .mode .value ,
98- "RetentionTime" : Utils .timedelta_to_str (self .retention_time ),
99- "RetentionSize" : self .retention_size ,
100- "Compress" : self .compress ,
101- }
275+ result : Dict [str , Any ] = {"Persist" : self .persist }
276+ if self .logs is not None :
277+ result ["Logs" ] = self .logs .to_json ()
278+ if self .microsoft_logs is not None :
279+ result ["MicrosoftLogs" ] = self .microsoft_logs .to_json ()
280+ if self .admin_logs is not None :
281+ result ["AdminLogs" ] = self .admin_logs .to_json ()
282+ return result
283+
284+ def __init__ (self , configuration , persist : bool = False ) -> None :
285+ if configuration is None :
286+ raise ValueError ("Configuration cannot be None" )
102287
103- def __init__ (self , parameters : Parameters ) -> None :
104- if parameters is None :
105- raise ValueError ("Parameters cannot be None" )
288+ parameters = SetLogsConfigurationOperation .Parameters (persist = persist )
289+ if isinstance (configuration , SetLogsConfigurationOperation .LogsConfiguration ):
290+ parameters .logs = configuration
291+ elif isinstance (configuration , SetLogsConfigurationOperation .MicrosoftLogsConfiguration ):
292+ parameters .microsoft_logs = configuration
293+ elif isinstance (configuration , SetLogsConfigurationOperation .AdminLogsConfiguration ):
294+ parameters .admin_logs = configuration
295+ else :
296+ raise TypeError ("Unsupported configuration type: " + type (configuration ).__name__ )
106297
107298 self ._parameters = parameters
108299
@@ -112,7 +303,7 @@ def get_command(self, conventions: "DocumentConventions") -> "VoidRavenCommand":
112303 class SetLogsConfigurationCommand (VoidRavenCommand ):
113304 def __init__ (self , parameters : "SetLogsConfigurationOperation.Parameters" ):
114305 if parameters is None :
115- raise ValueError ("Parameters must be None" )
306+ raise ValueError ("Parameters cannot be None" )
116307
117308 super ().__init__ ()
118309 self ._parameters = parameters
0 commit comments