MapleX 3.1.2__tar.gz → 3.2.0.dev1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. {maplex-3.1.2/src/MapleX.egg-info → maplex-3.2.0.dev1}/PKG-INFO +1 -1
  2. {maplex-3.1.2 → maplex-3.2.0.dev1}/pyproject.toml +1 -1
  3. {maplex-3.1.2 → maplex-3.2.0.dev1}/readmes/LoggingBestPractice.md +1 -1
  4. maplex-3.2.0.dev1/readmes/README_Logger.md +243 -0
  5. {maplex-3.1.2 → maplex-3.2.0.dev1/src/MapleX.egg-info}/PKG-INFO +1 -1
  6. {maplex-3.1.2 → maplex-3.2.0.dev1}/src/MapleX.egg-info/SOURCES.txt +4 -1
  7. {maplex-3.1.2 → maplex-3.2.0.dev1}/src/maplex/__init__.py +1 -1
  8. {maplex-3.1.2 → maplex-3.2.0.dev1}/src/maplex/library/logger/__init__.py +2 -1
  9. {maplex-3.1.2 → maplex-3.2.0.dev1}/src/maplex/library/logger/config.py +106 -16
  10. maplex-3.2.0.dev1/src/maplex/library/logger/consts/__init__.py +41 -0
  11. maplex-3.2.0.dev1/src/maplex/library/logger/consts/config_keys.py +20 -0
  12. maplex-3.2.0.dev1/src/maplex/library/logger/consts/defaults.py +4 -0
  13. maplex-3.2.0.dev1/src/maplex/library/logger/consts/dict_keys.py +11 -0
  14. {maplex-3.1.2 → maplex-3.2.0.dev1}/src/maplex/library/logger/formatter.py +9 -6
  15. {maplex-3.1.2 → maplex-3.2.0.dev1}/src/maplex/mapleLogger.py +23 -11
  16. maplex-3.1.2/readmes/README_Logger.md +0 -158
  17. maplex-3.1.2/src/maplex/library/logger/consts.py +0 -28
  18. {maplex-3.1.2 → maplex-3.2.0.dev1}/LICENSE +0 -0
  19. {maplex-3.1.2 → maplex-3.2.0.dev1}/MANIFEST.in +0 -0
  20. {maplex-3.1.2 → maplex-3.2.0.dev1}/README.md +0 -0
  21. {maplex-3.1.2 → maplex-3.2.0.dev1}/logErrorOutputSample.png +0 -0
  22. {maplex-3.1.2 → maplex-3.2.0.dev1}/logOutputSample.png +0 -0
  23. {maplex-3.1.2 → maplex-3.2.0.dev1}/readmes/README_ConsoleColors.md +0 -0
  24. {maplex-3.1.2 → maplex-3.2.0.dev1}/readmes/README_Exceptions.md +0 -0
  25. {maplex-3.1.2 → maplex-3.2.0.dev1}/readmes/README_Json.md +0 -0
  26. {maplex-3.1.2 → maplex-3.2.0.dev1}/readmes/README_MapleTree.md +0 -0
  27. {maplex-3.1.2 → maplex-3.2.0.dev1}/setup.cfg +0 -0
  28. {maplex-3.1.2 → maplex-3.2.0.dev1}/src/MapleX.egg-info/dependency_links.txt +0 -0
  29. {maplex-3.1.2 → maplex-3.2.0.dev1}/src/MapleX.egg-info/requires.txt +0 -0
  30. {maplex-3.1.2 → maplex-3.2.0.dev1}/src/MapleX.egg-info/top_level.txt +0 -0
  31. {maplex-3.1.2 → maplex-3.2.0.dev1}/src/maplex/jsonHandler.py +0 -0
  32. {maplex-3.1.2 → maplex-3.2.0.dev1}/src/maplex/library/logger/file_handler.py +0 -0
  33. {maplex-3.1.2 → maplex-3.2.0.dev1}/src/maplex/library/logger/log_levels.py +0 -0
  34. {maplex-3.1.2 → maplex-3.2.0.dev1}/src/maplex/library/logger/utilities.py +0 -0
  35. {maplex-3.1.2 → maplex-3.2.0.dev1}/src/maplex/mapleColors.py +0 -0
  36. {maplex-3.1.2 → maplex-3.2.0.dev1}/src/maplex/mapleExceptions.py +0 -0
  37. {maplex-3.1.2 → maplex-3.2.0.dev1}/src/maplex/mapleTreeEditor.py +0 -0
  38. {maplex-3.1.2 → maplex-3.2.0.dev1}/src/maplex/utils.py +0 -0
  39. {maplex-3.1.2 → maplex-3.2.0.dev1}/tests/test_logger_unittest.py +0 -0
  40. {maplex-3.1.2 → maplex-3.2.0.dev1}/tests/test_maplejson_unittest.py +0 -0
  41. {maplex-3.1.2 → maplex-3.2.0.dev1}/tests/test_mapletree_unittest.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: MapleX
3
- Version: 3.1.2
3
+ Version: 3.2.0.dev1
4
4
  Summary: A Python library for simple logging, json file operations, Maple file format operations, and console color utilities.
5
5
  Author: Ryuji Hazama
6
6
  Project-URL: PyPI, https://pypi.org/project/MapleX/
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "MapleX"
7
- version = "3.1.2"
7
+ version = "3.2.0.dev1"
8
8
  description = """A Python library for simple logging, json file operations, Maple file format operations, and console color utilities."""
9
9
  keywords = ["logging", "logger", "json", "file operations", "maple file format", "console colors"]
10
10
  readme = "README.md"
@@ -101,7 +101,7 @@
101
101
 
102
102
  ### `NONE`
103
103
 
104
-     The `NONE` level is a special level in `MapleX` that not commonly exists in other logging libraries. It is used to bypass all the log level filters and always output the log, or suppress all other log levels if you set this in the logger configuration. This is useful for debugging in production environments, where you want to log a specific event, but don't want to log any other lower level events under the same logger configuration. For example, you can set the log level to `NONE` for a specific logger to always log suspicious events, while setting the log level to `WARNING` for other loggers to only log warnings and errors.
104
+     The `NONE` level is a special level in `MapleX` that not commonly exists in other logging libraries. It is used to bypass all the log level filters and always output the log, or suppress all other log levels if you set this in the logger configuration. This is useful for debugging in production environments, where you want to log a specific event, but don't want to log any other lower level events under the same logger configuration. For example, you can set the log level to `NONE` for a specific logger to always log suspicious events, while setting the log level to `WARN` for other loggers to only log warnings and errors.
105
105
 
106
106
  ## Conclusion
107
107
 
@@ -0,0 +1,243 @@
1
+ # Logger Class
2
+
3
+     Logger class is a logging object for Python applications. It outputs application logs to log files and to standard output.
4
+
5
+ ## Logger Initialization
6
+
7
+ ```python
8
+ def __init__(
9
+ func: str = "",
10
+ workingDirectory: str | None = None,
11
+ cmdLogLevel: str | None = None,
12
+ fileLogLevel: str | None = None,
13
+ maxLogSize: float | None = None,
14
+ fileMode: Literal["append", "overwrite", "daily"] | None = None,
15
+ configFile: str = "config.json",
16
+ encoding: str | None = None,
17
+ ) -> None:
18
+ ```
19
+
20
+ |Property|Required|Value|Version|
21
+ |--------|--------|-----|-------|
22
+ |**`func`**||Primary function name||
23
+ |**`workingDirectory`**||Log file output directory||
24
+ |**`cmdLogLevel`**||Terminal output log level||
25
+ |**`fileLogLevel`**||Log file output log level||
26
+ |**`maxLogSize`**||Log file max size (MB)||
27
+ |**`fileMode`**||Logging file mode|`v3.0`|
28
+ |**`configFile`**||Logger configuration file path|`v3.0`|
29
+ |**`encoding`**||Log file encoding|`v3.0`|
30
+
31
+     The parameter overwrites the settings configured in `config.mpl`.
32
+
33
+ ## Functions
34
+
35
+ ### `getLogger()`
36
+
37
+ ```python
38
+ def getLogger(
39
+ name = "",
40
+ **kwargs
41
+ ) -> maplex.Logger:
42
+ ```
43
+
44
+ |Property|Required|Value|Version|
45
+ |--------|--------|-----|-------|
46
+ |**`name`**||Primary funcion name|`v3.0`|
47
+ |**`**kwargs`**||Other parameters|`v3.0`|
48
+
49
+     This get or creates a Logger instance.
50
+
51
+     If you already have a Logger class instance with the same name, the function returns the existing instance, and you can save your resources on the machine.
52
+
53
+ ```python
54
+ from maplex
55
+
56
+ logger = maplex.getLogger(__name__)
57
+ ```
58
+
59
+ ## Getters and Setters
60
+
61
+     Every class parameter has its own getter and setter functions, and you can set, change, or get those values after initializing the class.
62
+
63
+ ## Logging Methods
64
+
65
+ ```python
66
+ def trace(object: any) -> None:
67
+ def debug(object: any) -> None:
68
+ def info(object: any) -> None:
69
+ def warn(object: any) -> None:
70
+ def error(object: any, exception: Exception | None = None) -> None:
71
+ def fatal(object: any, exception: Exception | None = None) -> None:
72
+ ```
73
+
74
+ - You can use `exception` parameter to log the exception details along with a custom message in `error` and `fatal` methods in `v3.2.0` or later.
75
+
76
+     Each function outputs the log in each log level.
77
+
78
+ ## `ShowError` Function
79
+
80
+     This outputs the error logs and stuck trace.
81
+
82
+ Function:
83
+
84
+ ```python
85
+ def ShowError(
86
+ ex: Exception,
87
+ message: str | None = None,
88
+ fatal: bool = False
89
+ )
90
+ ```
91
+
92
+ |Property|Required|Value|
93
+ |--------|--------|-----|
94
+ |**`ex`**|\*|Exception|
95
+ |**`message`**||Custom error message|
96
+ |**`fatal`**||Show error as `FATAL`|
97
+
98
+ - If `fatal=True`, it outputs log as a `FATAL` log level.
99
+
100
+ ## Usage
101
+
102
+ ```python
103
+ from maplex
104
+
105
+ logger = maplex.getLogger("FunctionName")
106
+ logger.info("Hello there!")
107
+ ```
108
+
109
+ This outputs:
110
+
111
+ ```console
112
+ [INFO ][FunctionName] <module>(4) Hello there!
113
+ ```
114
+
115
+ File output will be: `AppLog.log`
116
+
117
+ ```log
118
+ (PsNo) yyyy-MM-dd HH:mm:ss.fff [INFO ][FunctionName] <module>(4) Hello there!
119
+ ```
120
+
121
+ ### Log Level
122
+
123
+ - `TRACE`
124
+ - `DEBUG`
125
+ - `INFO`
126
+ - `WARN`
127
+ - `ERROR`
128
+ - `FATAL`
129
+
130
+ ## Settings
131
+
132
+ - You can configure log settings with a JSON formatted file (default: `config.json`).
133
+ - If the configuration file does not exist, the instance auto-generates the file.
134
+ - Instance uses the parameter values to auto-generate a configuration file, or uses the default value if it was not specified.
135
+
136
+ Auto-generated configuration file (parameters not specified):
137
+
138
+ ```json
139
+ {
140
+ "MapleLogger": {
141
+ "ConsoleLogLevel": "INFO",
142
+ "FileLogLevel": "INFO",
143
+ "MaxLogSize": 3,
144
+ "WorkingDirectory": "logs",
145
+ "FileEncoding": "utf-8",
146
+ "Formats": {
147
+ "Timestamp": {
148
+ "Format": "%F %X.%f",
149
+ "Digits": null
150
+ },
151
+ "ConsoleLogFormat": "[{level}]{func} {callerFunc}{callerLine}",
152
+ "FileLogFormat": "({pid}) {timestamp} [{level}]{func} {callerName}{callerFunc}({callerLine})"
153
+ }
154
+ }
155
+ }
156
+ ```
157
+
158
+ |Key|Value|
159
+ |---|-----|
160
+ |**`ConsoleLogLevel`**|Console log level|
161
+ |**`FileLogLevel`**|File log level|
162
+ |**`MaxLogSize`**|Log file max size (MB)|
163
+ |**`WorkingDirectory`**|Log file output path|
164
+ |**`FileEncoding`**|Log file encoding|
165
+ |**`Formats`**|Log format settings|
166
+ |**`NameSpaces`**|Namespace specific log level settings|
167
+
168
+ - To disable the log output, set the log level to `NONE`.
169
+ - You can use a `float` number for the file max size (E.g., `2.5` for `2.5MB`)
170
+ - You can also use a `str` for the file max size (E.g., `"3M"`)
171
+ - You can set the timestamp format with the `Format` key in the `Timestamp` section of the `Formats` settings.
172
+ - The default format is `%F %X.%f`, which outputs the timestamp as `yyyy-MM-dd HH:mm:ss.fff`. You can use any valid Python datetime format string.
173
+ - You can set the number of digits for the timestamp with the `Digits` key in the `Timestamp` section of the `Formats` settings.
174
+
175
+ ### Formats
176
+
177
+ &nbsp;&nbsp;&nbsp;&nbsp;`v3.2.0` or later
178
+
179
+ - You can set the log format for console and file output with the `ConsoleLogFormat` and `FileLogFormat` keys in the `Formats` settings.
180
+ - Also, you can set the timestamp format with the `Format` key in the `Timestamp` section of the `Formats` settings.
181
+
182
+ ```json
183
+ {
184
+ "MapleLogger": {
185
+ "Formats": {
186
+ "Timestamp": {
187
+ "Format": "%F %X.%f",
188
+ "Digits": -3
189
+ },
190
+ "ConsoleLogFormat": "[{level}]{func} {callerFunc}{callerLine}",
191
+ "FileLogFormat": "({pid}) {timestamp} [{level}]{func} {callerName}{callerFunc}({callerLine})"
192
+ }
193
+ }
194
+ }
195
+ ```
196
+
197
+ |Key|Value|
198
+ |---|-----|
199
+ |**`Timestamp.Format`**|Timestamp format string|
200
+ |**`Timestamp.Digits`**|Number of digits for the timestamp|
201
+ |**`ConsoleLogFormat`**|Console log format string|
202
+ |**`FileLogFormat`**|File log format string|
203
+
204
+ - You can use standard Python datetime format strings for the `Timestamp.Format` key. For example, `%F %X.%f` outputs the timestamp as `yyyy-MM-dd HH:mm:ss.fff`.
205
+ - You can set the number of digits for the timestamp with the `Timestamp.Digits` key.
206
+ - The default value is `null`, which outputs the full length of the timestamp.
207
+ - However, I recommend using `-3` to output the timestamp with milliseconds, which is the most common format for logging.
208
+ - You can use a negative number to specify the number of digits to output from the end of the timestamp. For example, `-3` cuts the last three digits of the timestamp, which outputs the timestamp as `yyyy-MM-dd HH:mm:ss.fff`.
209
+ - You can use the following placeholders in the log format strings:
210
+
211
+ `{pid}`: Process ID
212
+ `{timestamp}`: Timestamp
213
+ `{level}`: Log level
214
+ `{func}`: Function name
215
+ `{callerName}`: Caller function name
216
+ `{callerFunc}`: Caller function name (with module name)
217
+ `{callerLine}`: Caller line number
218
+
219
+ ### Namespace Specific Log Level Settings
220
+
221
+ - You can set the log level for specific namespaces by adding a `NameSpaces` key in the configuration file.
222
+ - The `NameSpaces` key is a list of dictionaries, where each dictionary contains a `NameSpace`, a `ConsoleLogLevel`, and a `FileLogLevel`.
223
+
224
+ Example:
225
+
226
+ ```json
227
+ {
228
+ "MapleLogger": {
229
+ "ConsoleLogLevel": "INFO",
230
+ "FileLogLevel": "INFO",
231
+ "NameSpaces": [
232
+ {
233
+ "NameSpace": "MyNamespace",
234
+ "ConsoleLogLevel": "DEBUG",
235
+ "FileLogLevel": "INFO"
236
+ }
237
+ ]
238
+ }
239
+ }
240
+ ```
241
+
242
+ - In this example, the log level for the `MyNamespace` namespace is set to `DEBUG` for console output and `INFO` for file output. All other namespaces will use the default log levels specified in the `ConsoleLogLevel` and `FileLogLevel` keys.
243
+ - You can use this setting to control the log output for different parts of your application, allowing you to have more detailed logs for specific namespaces while keeping the overall log output at a higher level.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: MapleX
3
- Version: 3.1.2
3
+ Version: 3.2.0.dev1
4
4
  Summary: A Python library for simple logging, json file operations, Maple file format operations, and console color utilities.
5
5
  Author: Ryuji Hazama
6
6
  Project-URL: PyPI, https://pypi.org/project/MapleX/
@@ -24,11 +24,14 @@ src/maplex/mapleTreeEditor.py
24
24
  src/maplex/utils.py
25
25
  src/maplex/library/logger/__init__.py
26
26
  src/maplex/library/logger/config.py
27
- src/maplex/library/logger/consts.py
28
27
  src/maplex/library/logger/file_handler.py
29
28
  src/maplex/library/logger/formatter.py
30
29
  src/maplex/library/logger/log_levels.py
31
30
  src/maplex/library/logger/utilities.py
31
+ src/maplex/library/logger/consts/__init__.py
32
+ src/maplex/library/logger/consts/config_keys.py
33
+ src/maplex/library/logger/consts/defaults.py
34
+ src/maplex/library/logger/consts/dict_keys.py
32
35
  tests/test_logger_unittest.py
33
36
  tests/test_maplejson_unittest.py
34
37
  tests/test_mapletree_unittest.py
@@ -52,7 +52,7 @@ __all__ = [
52
52
  'winUnHide'
53
53
  ]
54
54
 
55
- __version__ = "3.1.2"
55
+ __version__ = "3.2.0.dev1"
56
56
  __author__ = "Ryuji Hazama"
57
57
  __license__ = "MIT"
58
58
 
@@ -2,7 +2,7 @@
2
2
  Split mapleLogger.py into multiple files for better organization and maintainability.
3
3
  """
4
4
 
5
- from .config import LoggerConfig
5
+ from .config import LoggerConfig, TimeStamp
6
6
  from .file_handler import FileHandler
7
7
  from .formatter import Formatter
8
8
  from .log_levels import LogLevel
@@ -14,6 +14,7 @@ __all__ = [
14
14
  "LoggerConfig",
15
15
  "LogLevel",
16
16
  "getConsoleColors",
17
+ "TimeStamp",
17
18
  "toLogLevel",
18
19
  "toLogSize",
19
20
  "isLogLevel"
@@ -21,6 +21,17 @@ from .utilities import *
21
21
  from library.logger.log_levels import LogLevel
22
22
 
23
23
 
24
+ class TimeStamp(BaseModel):
25
+
26
+ timestampFormat: str | None = None
27
+ digits: int | None = None
28
+
29
+ class Formats(BaseModel):
30
+
31
+ consoleFormat: str | None = None
32
+ fileFormat: str | None = None
33
+ timestamp: TimeStamp | None = None
34
+
24
35
  class LoggerConfig(BaseModel):
25
36
 
26
37
  func: str | None = None
@@ -32,12 +43,10 @@ class LoggerConfig(BaseModel):
32
43
  fileMode: Literal["append", "overwrite", "daily"] | None = None
33
44
  configFile: str = "config.json"
34
45
  encoding: str | None = None
35
- timestampFormat: str | None = None
36
46
  getLogger: bool | None = None
37
47
  consoleAlignWidth: int | None = None
38
48
  fileAlignWidth: int | None = None
39
- consoleFormat: str = CONSOLE_FORMAT
40
- fileFormat: str = FILE_FORMAT
49
+ formats: Formats | None = None
41
50
 
42
51
  logConfInstance: None = None
43
52
  logConf: dict[str, object] | None = None
@@ -52,6 +61,7 @@ class LoggerConfig(BaseModel):
52
61
 
53
62
  super().__init__(**config)
54
63
 
64
+ print("Initializing logger config...")
55
65
  self.consoleColors = getConsoleColors()
56
66
 
57
67
  self.logConfInstance = self.checkConfigFile(config.get(CONFIG_FILE, self.configFile))
@@ -62,11 +72,13 @@ class LoggerConfig(BaseModel):
62
72
  self.setLogFileSize(config.get(MAX_LOG_SIZE, None))
63
73
  self.setOutputLogLevels(config.get(CONSOLE_LOG_LEVEL, None), config.get(FILE_LOG_LEVEL, None))
64
74
  self.setFileEncoding(config.get(FILE_ENCODING, None))
65
- self.setTimestampFormat(config.get(TIMESTAMP_FORMAT, None))
75
+ self.setTimestampFormat(config.get(PARAM_TIMESTAMP, None))
76
+ self.setOutputFormat(config.get(CONSOLE_LOG_FORMAT, None), config.get(FILE_LOG_FORMAT, None))
66
77
  self.saveLogSettings(self.logConfInstance)
67
78
 
68
79
  except Exception as ex:
69
80
 
81
+ print(ex)
70
82
  print(f"{self.consoleColors.Red}Error: Failed to initialize logger config: {ex}{self.consoleColors.Reset}")
71
83
  raise ex
72
84
 
@@ -245,10 +257,26 @@ class LoggerConfig(BaseModel):
245
257
  print(f"{self.consoleColors.Red}Warning: Infinite log file size is not recommended. Using default value.{self.consoleColors.Reset}")
246
258
  self.maxLogSize = 3000000
247
259
 
260
+ def getLogLevel(self) -> None:
261
+
262
+ '''
263
+ Get LogLevel configuration from config file.
264
+ Set namespace specific LogLevel if available, otherwise set default LogLevel.
265
+ '''
266
+
267
+ namespaceSettings = self.logConf.get(NAME_SPACES, [])
268
+ thisNamespaceSettings = next((ns for ns in namespaceSettings if ns.get(NAME_SPACE) == self.callerName), None)
269
+
270
+ if thisNamespaceSettings is not None:
271
+
272
+ self.consoleLogLevel = thisNamespaceSettings.get(CONSOLE_LOG_LEVEL, None)
273
+ self.fileLogLevel = thisNamespaceSettings.get(FILE_LOG_LEVEL, None)
274
+
248
275
  def setOutputLogLevels(self, cmdLogLevel: object, fileLogLevel: object) -> None:
249
276
 
250
- self.consoleLogLevel = self.__setLogLevel(CONSOLE_LOG_LEVEL, cmdLogLevel)
251
- self.fileLogLevel = self.__setLogLevel(FILE_LOG_LEVEL, fileLogLevel)
277
+ self.getLogLevel()
278
+ self.consoleLogLevel = self.__setLogLevel(CONSOLE_LOG_LEVEL, cmdLogLevel if self.consoleLogLevel is None else self.consoleLogLevel)
279
+ self.fileLogLevel = self.__setLogLevel(FILE_LOG_LEVEL, fileLogLevel if self.fileLogLevel is None else self.fileLogLevel)
252
280
 
253
281
  def __setLogLevel(self, fileOrConsole, loglevel: object) -> LogLevel:
254
282
 
@@ -260,7 +288,7 @@ class LoggerConfig(BaseModel):
260
288
 
261
289
  else:
262
290
 
263
- tempLogLevel = self.logConf.get(fileOrConsole, "INFO")
291
+ tempLogLevel = self.logConf.get(fileOrConsole, None)
264
292
 
265
293
  if tempLogLevel is None:
266
294
 
@@ -293,24 +321,86 @@ class LoggerConfig(BaseModel):
293
321
 
294
322
  self.encoding = fileEncoding
295
323
 
296
- def setTimestampFormat(self, timestampFormat: str) -> None:
324
+ def setTimestampFormat(self, timestampFormat: dict) -> None:
297
325
 
298
326
  """Set timestamp format for logs. Default is "%F %X.%f" (e.g. 2024-06-01 12:34:56.789). You can set this in config file with key "TimestampFormat"."""
299
327
 
328
+ if self.formats is None:
329
+
330
+ self.formats = Formats()
331
+
332
+ if self.formats.timestamp is None:
333
+
334
+ self.formats.timestamp = TimeStamp()
335
+
300
336
  if timestampFormat is not None:
301
337
 
302
- self.timestampFormat = timestampFormat
338
+ self.formats.timestamp.timestampFormat = timestampFormat.get(FORMAT, None)
339
+ self.formats.timestamp.digits = timestampFormat.get(DIGITS, 3)
303
340
 
304
341
  else:
305
342
 
306
- configTimestampFormat = self.logConf.get(TIMESTAMP_FORMAT, None)
343
+ timestampSettings = self.logConf.get(FORMATS, {}).get(TIMESTAMP, {})
344
+ configTimestampFormat = timestampSettings.get(FORMAT, None)
345
+ configTimestampDigits = timestampSettings.get(DIGITS, None)
307
346
 
308
347
  if configTimestampFormat is None:
309
348
 
310
- configTimestampFormat = "%F %X.%f"
311
- self.logConf[TIMESTAMP_FORMAT] = configTimestampFormat
349
+ configTimestampFormat = TIMESTAMP_FORMAT
350
+ timestampSettings[FORMAT] = configTimestampFormat
351
+ self.logConf.setdefault(FORMATS, {})[TIMESTAMP] = timestampSettings
352
+
353
+ if configTimestampDigits is None:
354
+
355
+ configTimestampDigits = MILLISECOND_DIGITS
356
+ timestampSettings[DIGITS] = configTimestampDigits
357
+ self.logConf.setdefault(FORMATS, {})[TIMESTAMP] = timestampSettings
358
+
359
+ if "TimestampFormat" in self.logConf:
360
+
361
+ # Show warning if old config key is used
362
+ print(f"{self.consoleColors.Yellow}Warning: 'TimestampFormat' key in config file is deprecated. Please use 'Formats.Timestamp' instead.{self.consoleColors.Reset}\nMore details: https://github.com/Ryuji-Hazama/MapleTree/blob/main/readmes/README_Logger.md#settings")
363
+
364
+ self.formats.timestamp.timestampFormat = configTimestampFormat
365
+ self.formats.timestamp.digits = configTimestampDigits
366
+
367
+ def setOutputFormat(self, consoleFormat: str, fileFormat: str) -> None:
368
+
369
+ """Set output format for console and file logs. Default is "[{level}]{func} {callerFunc}{callerLine}" for console and "({pid}) {timestamp} [{level}]{func} {callerName}{callerFunc}({callerLine})" for file. You can set this in config file with keys "ConsoleLogFormat" and "FileLogFormat"."""
370
+
371
+ if self.formats is None:
372
+
373
+ self.formats = Formats()
374
+
375
+ if consoleFormat is not None:
376
+
377
+ self.formats.consoleFormat = consoleFormat
378
+
379
+ else:
380
+
381
+ self.formats.consoleFormat = self.logConf.get(CONSOLE_LOG_FORMAT, None)
382
+
383
+ if self.formats.consoleFormat is None:
384
+
385
+ self.formats.consoleFormat = CONSOLE_FORMAT
386
+ formatConf = self.logConf.get(FORMATS, {})
387
+ formatConf[CONSOLE_LOG_FORMAT] = CONSOLE_FORMAT
388
+ self.logConf[FORMATS] = formatConf
389
+
390
+ if fileFormat is not None:
391
+
392
+ self.formats.fileFormat = fileFormat
393
+
394
+ else:
395
+
396
+ self.formats.fileFormat = self.logConf.get(FILE_LOG_FORMAT, None)
397
+
398
+ if self.formats.fileFormat is None:
312
399
 
313
- self.timestampFormat = configTimestampFormat
400
+ self.formats.fileFormat = FILE_FORMAT
401
+ formatConf = self.logConf.get(FORMATS, {})
402
+ formatConf[FILE_LOG_FORMAT] = FILE_FORMAT
403
+ self.logConf[FORMATS] = formatConf
314
404
 
315
405
  def saveLogSettings(self, logConfInstance: MapleJson | None) -> None:
316
406
 
@@ -348,12 +438,12 @@ class LoggerConfig(BaseModel):
348
438
  MAX_LOG_SIZE: self.maxLogSize,
349
439
  FILE_MODE: "daily" if self.logfile and "log_" in self.logfile else "append",
350
440
  FILE_ENCODING: self.encoding,
351
- TIMESTAMP_FORMAT: self.timestampFormat,
441
+ TIMESTAMP: self.formats.timestamp if self.formats.timestamp else None,
352
442
  GET_LOGGER: bool(self.func),
353
443
  CONSOLE_ALIGN_WIDTH: self.consoleAlignWidth,
354
444
  FILE_ALIGN_WIDTH: self.fileAlignWidth,
355
- CONSOLE_FORMAT: self.consoleFormat,
356
- FILE_FORMAT: self.fileFormat,
445
+ PARAM_CONSOLE_FORMAT: self.formats.consoleFormat,
446
+ PARAM_FILE_FORMAT: self.formats.fileFormat,
357
447
  PROCESS_ID: self.pid
358
448
  }
359
449
 
@@ -0,0 +1,41 @@
1
+ from .config_keys import *
2
+ from .defaults import *
3
+ from .dict_keys import *
4
+
5
+ __all__ = [
6
+ "CONFIG_KEY",
7
+ "CONSOLE",
8
+ "FILE",
9
+ "CONSOLE_LOG_LEVEL",
10
+ "FILE_LOG_LEVEL",
11
+ "MAX_LOG_SIZE",
12
+ "FILE_MODE",
13
+ "WORKING_DIRECTORY",
14
+ "FILE_ENCODING",
15
+ "FORMATS",
16
+ "TIMESTAMP",
17
+ "FORMAT",
18
+ "DIGITS",
19
+ "ALIGN_WIDTH",
20
+ "CONSOLE_LOG_FORMAT",
21
+ "FILE_LOG_FORMAT",
22
+ "NAME_SPACES",
23
+ "NAME_SPACE",
24
+ # Dictionary keys for internal use
25
+ "GET_LOGGER",
26
+ "FUNC",
27
+ "CONFIG_FILE",
28
+ "CONSOLE_ALIGN_WIDTH",
29
+ "FILE_ALIGN_WIDTH",
30
+ "CALLER_NAME",
31
+ "PROCESS_ID",
32
+ "PARAM_TIMESTAMP",
33
+ "PARAM_FORMATS",
34
+ "PARAM_CONSOLE_FORMAT",
35
+ "PARAM_FILE_FORMAT",
36
+ # Default values
37
+ "FILE_FORMAT",
38
+ "CONSOLE_FORMAT",
39
+ "TIMESTAMP_FORMAT",
40
+ "MILLISECOND_DIGITS",
41
+ ]
@@ -0,0 +1,20 @@
1
+ CONFIG_KEY = "MapleLogger"
2
+ CONSOLE = "Console"
3
+ FILE = "File"
4
+ CONSOLE_LOG_LEVEL = "ConsoleLogLevel"
5
+ FILE_LOG_LEVEL = "FileLogLevel"
6
+ MAX_LOG_SIZE = "MaxLogSize"
7
+ FILE_MODE = "FileMode"
8
+ WORKING_DIRECTORY = "WorkingDirectory"
9
+ FILE_ENCODING = "FileEncoding"
10
+
11
+ FORMATS = "Formats"
12
+ CONSOLE_LOG_FORMAT = "ConsoleLogFormat"
13
+ FILE_LOG_FORMAT = "FileLogFormat"
14
+ TIMESTAMP = "Timestamp"
15
+ FORMAT = "Format"
16
+ DIGITS = "Digits"
17
+
18
+ ALIGN_WIDTH = "AlignWidth"
19
+ NAME_SPACES = "NameSpaces"
20
+ NAME_SPACE = "NameSpace"
@@ -0,0 +1,4 @@
1
+ FILE_FORMAT = '({pid}) {timestamp} [{level}]{func} {callerName}{callerFunc}({callerLine})'
2
+ CONSOLE_FORMAT = '[{level}]{func} {callerFunc}{callerLine}'
3
+ TIMESTAMP_FORMAT = "%F %X.%f"
4
+ MILLISECOND_DIGITS = None
@@ -0,0 +1,11 @@
1
+ CONFIG_FILE = "configFile"
2
+ FUNC = "func"
3
+ GET_LOGGER = "getLogger"
4
+ CONSOLE_ALIGN_WIDTH = "consoleAlignWidth"
5
+ FILE_ALIGN_WIDTH = "fileAlignWidth"
6
+ CALLER_NAME = "CallerName"
7
+ PROCESS_ID = "pid"
8
+ PARAM_TIMESTAMP = "timestamp"
9
+ PARAM_FORMATS = "formats"
10
+ PARAM_CONSOLE_FORMAT = "consoleFormat"
11
+ PARAM_FILE_FORMAT = "fileFormat"
@@ -7,6 +7,7 @@ from datetime import datetime
7
7
  import inspect
8
8
  import os
9
9
 
10
+ from .config import TimeStamp
10
11
  from .consts import *
11
12
  from .log_levels import *
12
13
  from .utilities import *
@@ -29,8 +30,10 @@ class Formatter:
29
30
  self.consoleColors = getConsoleColors()
30
31
  self.logLevel = LogLevel
31
32
  self.config = config
32
- self.consoleFormat = config.get(CONSOLE_FORMAT, CONSOLE_FORMAT)
33
- self.fileFormat = config.get(FILE_FORMAT, FILE_FORMAT)
33
+ self.consoleFormat = config.get(PARAM_CONSOLE_FORMAT, CONSOLE_FORMAT)
34
+ self.fileFormat = config.get(PARAM_FILE_FORMAT, FILE_FORMAT)
35
+ self.timestampFormat = self.config.get(TIMESTAMP, TimeStamp()).timestampFormat or TIMESTAMP_FORMAT
36
+ self.timestampDigits = config.get(TIMESTAMP, TimeStamp()).digits or MILLISECOND_DIGITS
34
37
  self.pid = os.getpid()
35
38
  self.COLOR_CODE_LENGTH = 5
36
39
  self.COLOR_CODE_RESET_LENGTH = 4
@@ -69,7 +72,7 @@ class Formatter:
69
72
 
70
73
  return colorCodeLength
71
74
 
72
- def __getCurrentTimestamp(self, timestampFormat: str) -> str:
75
+ def __getCurrentTimestamp(self) -> str:
73
76
 
74
77
  """
75
78
  Get the current timestamp formatted according to the specified format string.
@@ -78,7 +81,7 @@ class Formatter:
78
81
  :return: The current timestamp as a formatted string.
79
82
  """
80
83
 
81
- return datetime.now().strftime(timestampFormat)
84
+ return datetime.now().strftime(self.timestampFormat)[:self.timestampDigits]
82
85
 
83
86
  def __getLogLevelColor(self, logLevel: LogLevel) -> str:
84
87
 
@@ -142,7 +145,7 @@ class Formatter:
142
145
 
143
146
  if '{timestamp}' in consoleFormat:
144
147
 
145
- timestamp = self.__getCurrentTimestamp(self.config.get(TIMESTAMP_FORMAT, '%Y-%m-%d %H:%M:%S'))
148
+ timestamp = self.__getCurrentTimestamp()
146
149
  consoleFormat = consoleFormat.replace('{timestamp}', timestamp)
147
150
 
148
151
  if '{level}' in consoleFormat:
@@ -193,7 +196,7 @@ class Formatter:
193
196
 
194
197
  if '{timestamp}' in fileFormat:
195
198
 
196
- timestamp = self.__getCurrentTimestamp(self.config.get(TIMESTAMP_FORMAT, '%Y-%m-%d %H:%M:%S'))
199
+ timestamp = self.__getCurrentTimestamp()
197
200
  fileFormat = fileFormat.replace('{timestamp}', timestamp)
198
201
 
199
202
  if '{level}' in fileFormat:
@@ -28,7 +28,7 @@ class Logger:
28
28
  fileMode: Literal["append", "overwrite", "daily"] | None = None,
29
29
  configFile: str = "config.json",
30
30
  encoding: str | None = None,
31
- timestampFormat: str | None = None,
31
+ timestamp: TimeStamp | None = None,
32
32
  **kwargs
33
33
  ) -> None:
34
34
 
@@ -48,7 +48,7 @@ class Logger:
48
48
  "fileMode": fileMode,
49
49
  "configFile": configFile,
50
50
  "encoding": encoding,
51
- "timestampFormat": timestampFormat,
51
+ "timestamp": timestamp,
52
52
  "getLogger": kwargs.get("getLogger", False),
53
53
  "consoleAlignWidth": kwargs.get("consoleAlignWidth", 16),
54
54
  "fileAlignWidth": kwargs.get("fileAlignWidth", 4)
@@ -235,21 +235,33 @@ class Logger:
235
235
  ################################
236
236
  # Error
237
237
 
238
- def error(self, object: any):
238
+ def error(self, object: any, exception: Exception | None = None):
239
239
 
240
240
  '''Error log'''
241
241
 
242
- self.logWriter(self.LogLevel.ERROR, object, callerDepth=self.DEFAULT_CALLER_DEPTH)
242
+ if exception is None:
243
+
244
+ self.logWriter(self.LogLevel.ERROR, object, callerDepth=self.DEFAULT_CALLER_DEPTH)
245
+
246
+ else:
247
+
248
+ self.ShowError(exception, message=f"{object}", fatal=False, additionalCallerDepth=1)
243
249
 
244
250
  #
245
251
  ################################
246
252
  # Fatal
247
253
 
248
- def fatal(self, object: any):
254
+ def fatal(self, object: any, exception: Exception | None = None):
249
255
 
250
256
  '''Fatal log'''
251
257
 
252
- self.logWriter(self.LogLevel.FATAL, object, callerDepth=self.DEFAULT_CALLER_DEPTH)
258
+ if exception is None:
259
+
260
+ self.logWriter(self.LogLevel.FATAL, object, callerDepth=self.DEFAULT_CALLER_DEPTH)
261
+
262
+ else:
263
+
264
+ self.ShowError(exception, message=f"{object}", fatal=True, additionalCallerDepth=1)
253
265
 
254
266
  #
255
267
  ################################
@@ -265,7 +277,7 @@ class Logger:
265
277
  ################################
266
278
  # Error messages
267
279
 
268
- def ShowError(self, ex: Exception, message: str | None = None, fatal: bool = False):
280
+ def ShowError(self, ex: Exception, message: str | None = None, fatal: bool = False, additionalCallerDepth: int = 0) -> None:
269
281
 
270
282
  '''Show and log error'''
271
283
 
@@ -279,9 +291,9 @@ class Logger:
279
291
 
280
292
  if message is not None:
281
293
 
282
- self.logWriter(logLevel, message, callerDepth=self.DEFAULT_CALLER_DEPTH)
294
+ self.logWriter(logLevel, message, callerDepth=self.DEFAULT_CALLER_DEPTH + additionalCallerDepth)
283
295
 
284
- self.logWriter(logLevel, f"{ex}\n{traceback.format_exc()}", callerDepth=self.DEFAULT_CALLER_DEPTH)
296
+ self.logWriter(logLevel, f"{ex}\n{traceback.format_exc()}", callerDepth=self.DEFAULT_CALLER_DEPTH + additionalCallerDepth)
285
297
 
286
298
  #
287
299
  ################################
@@ -377,9 +389,9 @@ ToDo list:
377
389
 
378
390
  * Logger *
379
391
 
380
- - Add option to set date format
381
- - Add set* functions
382
392
  - Configure log format in config file
393
+ - Configure mili sec digits
394
+ - Add set* functions
383
395
 
384
396
  """
385
397
  """ * * * * * * * * * * * * * """
@@ -1,158 +0,0 @@
1
- # Logger Class
2
-
3
- &nbsp;&nbsp;&nbsp;&nbsp;Logger class is a logging object for Python applications. It outputs application logs to log files and to standard output.
4
-
5
- ## Logger Initialization
6
-
7
- ```python
8
- def __init__(
9
- func: str = "",
10
- workingDirectory: str | None = None,
11
- cmdLogLevel: str | None = None,
12
- fileLogLevel: str | None = None,
13
- maxLogSize: float | None = None,
14
- fileMode: Literal["append", "overwrite", "daily"] | None = None,
15
- configFile: str = "config.json",
16
- encoding: str | None = None,
17
- ) -> None:
18
- ```
19
-
20
- |Property|Required|Value|Version|
21
- |--------|--------|-----|-------|
22
- |**`func`**||Primary function name||
23
- |**`workingDirectory`**||Log file output directory||
24
- |**`cmdLogLevel`**||Terminal output log level||
25
- |**`fileLogLevel`**||Log file output log level||
26
- |**`maxLogSize`**||Log file max size (MB)||
27
- |**`fileMode`**||Logging file mode|`v3.0`|
28
- |**`configFile`**||Logger configuration file path|`v3.0`|
29
- |**`encoding`**||Log file encoding|`v3.0`|
30
-
31
- &nbsp;&nbsp;&nbsp;&nbsp;The parameter overwrites the settings configured in `config.mpl`.
32
-
33
- ## Functions
34
-
35
- ### `getLogger()`
36
-
37
- ```python
38
- def getLogger(
39
- name = "",
40
- **kwargs
41
- ) -> maplex.Logger:
42
- ```
43
-
44
- |Property|Required|Value|Version|
45
- |--------|--------|-----|-------|
46
- |**`name`**||Primary funcion name|`v3.0`|
47
- |**`**kwargs`**||Other parameters|`v3.0`|
48
-
49
- &nbsp;&nbsp;&nbsp;&nbsp;This get or creates a Logger instance.
50
-
51
- &nbsp;&nbsp;&nbsp;&nbsp;If you already have a Logger class instance with the same name, the function returns the existing instance, and you can save your resources on the machine.
52
-
53
- ```python
54
- from maplex
55
-
56
- logger = maplex.getLogger(__name__)
57
- ```
58
-
59
- ## Getters and Setters
60
-
61
- &nbsp;&nbsp;&nbsp;&nbsp;Every class parameter has its own getter and setter functions, and you can set, change, or get those values after initializing the class.
62
-
63
- ## Logging Methods
64
-
65
- ```python
66
- def trace(object: any) -> None:
67
- def debug(object: any) -> None:
68
- def info(object: any) -> None:
69
- def warn(object: any) -> None:
70
- def error(object: any) -> None:
71
- def fatal(object: any) -> None:
72
- ```
73
-
74
- &nbsp;&nbsp;&nbsp;&nbsp;Each function outputs the log in each log level.
75
-
76
- ## `ShowError` Function
77
-
78
- &nbsp;&nbsp;&nbsp;&nbsp;This outputs the error logs and stuck trace.
79
-
80
- Function:
81
-
82
- ```python
83
- def ShowError(
84
- ex: Exception,
85
- message: str | None = None,
86
- fatal: bool = False
87
- )
88
- ```
89
-
90
- |Property|Required|Value|
91
- |--------|--------|-----|
92
- |**`ex`**|\*|Exception|
93
- |**`message`**||Custom error message|
94
- |**`fatal`**||Show error as `FATAL`|
95
-
96
- - If `fatal=True`, it outputs log as a `FATAL` log level.
97
-
98
- ## Usage
99
-
100
- ```python
101
- from maplex
102
-
103
- logger = maplex.getLogger("FunctionName")
104
- logger.info("Hello there!")
105
- ```
106
-
107
- This outputs:
108
-
109
- ```console
110
- [INFO ][FunctionName] <module>(4) Hello there!
111
- ```
112
-
113
- File output will be: `AppLog.log`
114
-
115
- ```log
116
- (PsNo) yyyy-MM-dd HH:mm:ss.fff [INFO ][FunctionName] <module>(4) Hello there!
117
- ```
118
-
119
- ### Log Level
120
-
121
- - `TRACE`
122
- - `DEBUG`
123
- - `INFO`
124
- - `WARN`
125
- - `ERROR`
126
- - `FATAL`
127
-
128
- ## Settings
129
-
130
- - You can configure log settings with a JSON formatted file (default: `config.json`).
131
- - If the configuration file does not exist, the instance auto-generates the file.
132
- - Instance uses the parameter values to auto-generate a configuration file, or uses the default value if it was not specified.
133
-
134
- Auto-generated configuration file (parameters not specified):
135
-
136
- ```json
137
- {
138
- "MapleLogger": {
139
- "ConsoleLogLevel": "INFO",
140
- "FileLogLevel": "INFO",
141
- "MaxLogSize": 3.0,
142
- "WorkingDirectory": "/path/to/output/logs",
143
- "FileEncoding": "utf-8"
144
- }
145
- }
146
- ```
147
-
148
- |Key|Value|
149
- |---|-----|
150
- |**`ConsoleLogLevel`**|Console log level|
151
- |**`FileLogLevel`**|File log level|
152
- |**`MaxLogSize`**|Log file max size (MB)|
153
- |**`WorkingDirectory`**|Log file output path|
154
- |**`FileEncoding`**|Log file encoding|
155
-
156
- - To disable the log output, set the log level to `NONE`.
157
- - You can use a `float` number for the file max size (E.g., `2.5` for `2.5MB`)
158
- - You can also use a `str` for the file max size (E.g., `"3M"`)
@@ -1,28 +0,0 @@
1
-
2
- CONFIG_KEY = "MapleLogger"
3
- FUNC = "func"
4
- CONSOLE = "Console"
5
- FILE = "File"
6
- CONSOLE_LOG_LEVEL = "ConsoleLogLevel"
7
- FILE_LOG_LEVEL = "FileLogLevel"
8
- MAX_LOG_SIZE = "MaxLogSize"
9
- FILE_MODE = "FileMode"
10
- CONFIG_FILE = "configFile"
11
- WORKING_DIRECTORY = "WorkingDirectory"
12
- FILE_ENCODING = "FileEncoding"
13
- TIMESTAMP_FORMAT = "TimestampFormat"
14
- ALIGN_WIDTH = "AlignWidth"
15
- GET_LOGGER = "getLogger"
16
- CONSOLE_ALIGN_WIDTH = "consoleAlignWidth"
17
- FILE_ALIGN_WIDTH = "fileAlignWidth"
18
- CONSOLE_FORMAT = "ConsoleFormat"
19
- FILE_FORMAT = "FileFormat"
20
-
21
- CALLER_NAME = "CallerName"
22
- PROCESS_ID = "pid"
23
-
24
- FILE_FORMAT = '({pid}) {timestamp} [{level}]{func} {callerName}{callerFunc}({callerLine})'
25
- CONSOLE_FORMAT = '[{level}]{func} {callerFunc}{callerLine}'
26
-
27
- FILE_MODE_OVERWRITE = 'overwrite'
28
- FILE_MODE_DAILY = 'daily'
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes