maleo-logging 0.0.1__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.
@@ -0,0 +1,57 @@
1
+ # Proprietary Software License
2
+
3
+ **Copyright (c) 2025 Agra Bima Yuda / Nexmedis**
4
+
5
+ ## License Grant
6
+
7
+ This software and associated documentation files (the "Software") are proprietary and confidential to Agra Bima Yuda and/or Nexmedis ("Licensor"). All rights reserved.
8
+
9
+ ## Restrictions
10
+
11
+ **NO PERMISSION** is granted to any person to:
12
+
13
+ 1. **Use** the Software for any purpose without explicit written permission from the Licensor
14
+ 2. **Copy, modify, merge, publish, distribute, sublicense, or sell** copies of the Software
15
+ 3. **Reverse engineer, decompile, or disassemble** the Software
16
+ 4. **Create derivative works** based upon the Software
17
+ 5. **Remove or alter** any proprietary notices, labels, or marks on the Software
18
+
19
+ ## Permitted Use
20
+
21
+ Use of this Software is permitted only under the following conditions:
22
+
23
+ 1. **Authorized Users**: Only individuals or entities explicitly authorized by the Licensor in writing
24
+ 2. **Internal Use Only**: The Software may only be used for internal business purposes of the authorized entity
25
+ 3. **No Redistribution**: The Software may not be shared, distributed, or made available to any third party
26
+
27
+ ## Ownership
28
+
29
+ The Software is and remains the exclusive property of the Licensor. This license does not grant any ownership rights in the Software.
30
+
31
+ ## Confidentiality
32
+
33
+ The Software contains proprietary and confidential information. Recipients agree to:
34
+
35
+ 1. Maintain the confidentiality of the Software
36
+ 2. Use the same degree of care to protect the Software as they use for their own confidential information, but no less than reasonable care
37
+ 3. Not disclose the Software to any third party without prior written consent
38
+
39
+ ## Termination
40
+
41
+ This license is effective until terminated. The Licensor may terminate this license at any time without notice. Upon termination, all rights granted herein cease immediately, and the recipient must destroy all copies of the Software.
42
+
43
+ ## Disclaimer of Warranty
44
+
45
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
46
+
47
+ ## Limitation of Liability
48
+
49
+ IN NO EVENT SHALL THE LICENSOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
50
+
51
+ ## Governing Law
52
+
53
+ This license shall be governed by and construed in accordance with the laws of Indonesia, without regard to its conflict of law provisions.
54
+
55
+ ---
56
+
57
+ For licensing inquiries, contact: agra@nexmedis.com
@@ -0,0 +1,94 @@
1
+ Metadata-Version: 2.4
2
+ Name: maleo-logging
3
+ Version: 0.0.1
4
+ Summary: Logging package for MaleoSuite
5
+ Author-email: Agra Bima Yuda <agra@nexmedis.com>
6
+ License: Proprietary
7
+ Requires-Python: >=3.12
8
+ Description-Content-Type: text/markdown
9
+ License-File: LICENSE
10
+ Requires-Dist: annotated-types>=0.7.0
11
+ Requires-Dist: anyio>=4.10.0
12
+ Requires-Dist: black>=25.1.0
13
+ Requires-Dist: cachetools>=5.5.2
14
+ Requires-Dist: certifi>=2025.8.3
15
+ Requires-Dist: cffi>=1.17.1
16
+ Requires-Dist: cfgv>=3.4.0
17
+ Requires-Dist: charset-normalizer>=3.4.3
18
+ Requires-Dist: click>=8.2.1
19
+ Requires-Dist: cryptography>=45.0.6
20
+ Requires-Dist: distlib>=0.4.0
21
+ Requires-Dist: fastapi>=0.116.1
22
+ Requires-Dist: filelock>=3.19.1
23
+ Requires-Dist: google-api-core>=2.25.1
24
+ Requires-Dist: google-auth>=2.40.3
25
+ Requires-Dist: google-cloud-appengine-logging>=1.6.2
26
+ Requires-Dist: google-cloud-audit-log>=0.3.2
27
+ Requires-Dist: google-cloud-core>=2.4.3
28
+ Requires-Dist: google-cloud-logging>=3.12.1
29
+ Requires-Dist: googleapis-common-protos>=1.70.0
30
+ Requires-Dist: grpc-google-iam-v1>=0.14.2
31
+ Requires-Dist: grpcio>=1.74.0
32
+ Requires-Dist: grpcio-status>=1.74.0
33
+ Requires-Dist: identify>=2.6.13
34
+ Requires-Dist: idna>=3.10
35
+ Requires-Dist: importlib_metadata>=8.7.0
36
+ Requires-Dist: maleo-enums>=0.0.2
37
+ Requires-Dist: maleo-types-base>=0.0.2
38
+ Requires-Dist: maleo-utils>=0.0.3
39
+ Requires-Dist: mypy_extensions>=1.1.0
40
+ Requires-Dist: nodeenv>=1.9.1
41
+ Requires-Dist: opentelemetry-api>=1.36.0
42
+ Requires-Dist: packaging>=25.0
43
+ Requires-Dist: pathspec>=0.12.1
44
+ Requires-Dist: platformdirs>=4.4.0
45
+ Requires-Dist: pre_commit>=4.3.0
46
+ Requires-Dist: proto-plus>=1.26.1
47
+ Requires-Dist: protobuf>=6.32.0
48
+ Requires-Dist: pyasn1>=0.6.1
49
+ Requires-Dist: pyasn1_modules>=0.4.2
50
+ Requires-Dist: pycparser>=2.22
51
+ Requires-Dist: pycryptodome>=3.23.0
52
+ Requires-Dist: pydantic>=2.11.7
53
+ Requires-Dist: pydantic_core>=2.33.2
54
+ Requires-Dist: PyYAML>=6.0.2
55
+ Requires-Dist: requests>=2.32.5
56
+ Requires-Dist: rsa>=4.9.1
57
+ Requires-Dist: sniffio>=1.3.1
58
+ Requires-Dist: starlette>=0.47.3
59
+ Requires-Dist: typing-inspection>=0.4.1
60
+ Requires-Dist: typing_extensions>=4.15.0
61
+ Requires-Dist: urllib3>=2.5.0
62
+ Requires-Dist: virtualenv>=20.34.0
63
+ Requires-Dist: zipp>=3.23.0
64
+ Dynamic: license-file
65
+
66
+ # README #
67
+
68
+ This README would normally document whatever steps are necessary to get your application up and running.
69
+
70
+ ### What is this repository for? ###
71
+
72
+ * Quick summary
73
+ * Version
74
+ * [Learn Markdown](https://bitbucket.org/tutorials/markdowndemo)
75
+
76
+ ### How do I get set up? ###
77
+
78
+ * Summary of set up
79
+ * Configuration
80
+ * Dependencies
81
+ * Database configuration
82
+ * How to run tests
83
+ * Deployment instructions
84
+
85
+ ### Contribution guidelines ###
86
+
87
+ * Writing tests
88
+ * Code review
89
+ * Other guidelines
90
+
91
+ ### Who do I talk to? ###
92
+
93
+ * Repo owner or admin
94
+ * Other community or team contact
@@ -0,0 +1,29 @@
1
+ # README #
2
+
3
+ This README would normally document whatever steps are necessary to get your application up and running.
4
+
5
+ ### What is this repository for? ###
6
+
7
+ * Quick summary
8
+ * Version
9
+ * [Learn Markdown](https://bitbucket.org/tutorials/markdowndemo)
10
+
11
+ ### How do I get set up? ###
12
+
13
+ * Summary of set up
14
+ * Configuration
15
+ * Dependencies
16
+ * Database configuration
17
+ * How to run tests
18
+ * Deployment instructions
19
+
20
+ ### Contribution guidelines ###
21
+
22
+ * Writing tests
23
+ * Code review
24
+ * Other guidelines
25
+
26
+ ### Who do I talk to? ###
27
+
28
+ * Repo owner or admin
29
+ * Other community or team contact
@@ -0,0 +1,94 @@
1
+ Metadata-Version: 2.4
2
+ Name: maleo-logging
3
+ Version: 0.0.1
4
+ Summary: Logging package for MaleoSuite
5
+ Author-email: Agra Bima Yuda <agra@nexmedis.com>
6
+ License: Proprietary
7
+ Requires-Python: >=3.12
8
+ Description-Content-Type: text/markdown
9
+ License-File: LICENSE
10
+ Requires-Dist: annotated-types>=0.7.0
11
+ Requires-Dist: anyio>=4.10.0
12
+ Requires-Dist: black>=25.1.0
13
+ Requires-Dist: cachetools>=5.5.2
14
+ Requires-Dist: certifi>=2025.8.3
15
+ Requires-Dist: cffi>=1.17.1
16
+ Requires-Dist: cfgv>=3.4.0
17
+ Requires-Dist: charset-normalizer>=3.4.3
18
+ Requires-Dist: click>=8.2.1
19
+ Requires-Dist: cryptography>=45.0.6
20
+ Requires-Dist: distlib>=0.4.0
21
+ Requires-Dist: fastapi>=0.116.1
22
+ Requires-Dist: filelock>=3.19.1
23
+ Requires-Dist: google-api-core>=2.25.1
24
+ Requires-Dist: google-auth>=2.40.3
25
+ Requires-Dist: google-cloud-appengine-logging>=1.6.2
26
+ Requires-Dist: google-cloud-audit-log>=0.3.2
27
+ Requires-Dist: google-cloud-core>=2.4.3
28
+ Requires-Dist: google-cloud-logging>=3.12.1
29
+ Requires-Dist: googleapis-common-protos>=1.70.0
30
+ Requires-Dist: grpc-google-iam-v1>=0.14.2
31
+ Requires-Dist: grpcio>=1.74.0
32
+ Requires-Dist: grpcio-status>=1.74.0
33
+ Requires-Dist: identify>=2.6.13
34
+ Requires-Dist: idna>=3.10
35
+ Requires-Dist: importlib_metadata>=8.7.0
36
+ Requires-Dist: maleo-enums>=0.0.2
37
+ Requires-Dist: maleo-types-base>=0.0.2
38
+ Requires-Dist: maleo-utils>=0.0.3
39
+ Requires-Dist: mypy_extensions>=1.1.0
40
+ Requires-Dist: nodeenv>=1.9.1
41
+ Requires-Dist: opentelemetry-api>=1.36.0
42
+ Requires-Dist: packaging>=25.0
43
+ Requires-Dist: pathspec>=0.12.1
44
+ Requires-Dist: platformdirs>=4.4.0
45
+ Requires-Dist: pre_commit>=4.3.0
46
+ Requires-Dist: proto-plus>=1.26.1
47
+ Requires-Dist: protobuf>=6.32.0
48
+ Requires-Dist: pyasn1>=0.6.1
49
+ Requires-Dist: pyasn1_modules>=0.4.2
50
+ Requires-Dist: pycparser>=2.22
51
+ Requires-Dist: pycryptodome>=3.23.0
52
+ Requires-Dist: pydantic>=2.11.7
53
+ Requires-Dist: pydantic_core>=2.33.2
54
+ Requires-Dist: PyYAML>=6.0.2
55
+ Requires-Dist: requests>=2.32.5
56
+ Requires-Dist: rsa>=4.9.1
57
+ Requires-Dist: sniffio>=1.3.1
58
+ Requires-Dist: starlette>=0.47.3
59
+ Requires-Dist: typing-inspection>=0.4.1
60
+ Requires-Dist: typing_extensions>=4.15.0
61
+ Requires-Dist: urllib3>=2.5.0
62
+ Requires-Dist: virtualenv>=20.34.0
63
+ Requires-Dist: zipp>=3.23.0
64
+ Dynamic: license-file
65
+
66
+ # README #
67
+
68
+ This README would normally document whatever steps are necessary to get your application up and running.
69
+
70
+ ### What is this repository for? ###
71
+
72
+ * Quick summary
73
+ * Version
74
+ * [Learn Markdown](https://bitbucket.org/tutorials/markdowndemo)
75
+
76
+ ### How do I get set up? ###
77
+
78
+ * Summary of set up
79
+ * Configuration
80
+ * Dependencies
81
+ * Database configuration
82
+ * How to run tests
83
+ * Deployment instructions
84
+
85
+ ### Contribution guidelines ###
86
+
87
+ * Writing tests
88
+ * Code review
89
+ * Other guidelines
90
+
91
+ ### Who do I talk to? ###
92
+
93
+ * Repo owner or admin
94
+ * Other community or team contact
@@ -0,0 +1,13 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ maleo_logging.egg-info/PKG-INFO
5
+ maleo_logging.egg-info/SOURCES.txt
6
+ maleo_logging.egg-info/dependency_links.txt
7
+ maleo_logging.egg-info/requires.txt
8
+ maleo_logging.egg-info/top_level.txt
9
+ src/__init__.py
10
+ src/dtos.py
11
+ src/enums.py
12
+ src/google.py
13
+ src/logger.py
@@ -0,0 +1,54 @@
1
+ annotated-types>=0.7.0
2
+ anyio>=4.10.0
3
+ black>=25.1.0
4
+ cachetools>=5.5.2
5
+ certifi>=2025.8.3
6
+ cffi>=1.17.1
7
+ cfgv>=3.4.0
8
+ charset-normalizer>=3.4.3
9
+ click>=8.2.1
10
+ cryptography>=45.0.6
11
+ distlib>=0.4.0
12
+ fastapi>=0.116.1
13
+ filelock>=3.19.1
14
+ google-api-core>=2.25.1
15
+ google-auth>=2.40.3
16
+ google-cloud-appengine-logging>=1.6.2
17
+ google-cloud-audit-log>=0.3.2
18
+ google-cloud-core>=2.4.3
19
+ google-cloud-logging>=3.12.1
20
+ googleapis-common-protos>=1.70.0
21
+ grpc-google-iam-v1>=0.14.2
22
+ grpcio>=1.74.0
23
+ grpcio-status>=1.74.0
24
+ identify>=2.6.13
25
+ idna>=3.10
26
+ importlib_metadata>=8.7.0
27
+ maleo-enums>=0.0.2
28
+ maleo-types-base>=0.0.2
29
+ maleo-utils>=0.0.3
30
+ mypy_extensions>=1.1.0
31
+ nodeenv>=1.9.1
32
+ opentelemetry-api>=1.36.0
33
+ packaging>=25.0
34
+ pathspec>=0.12.1
35
+ platformdirs>=4.4.0
36
+ pre_commit>=4.3.0
37
+ proto-plus>=1.26.1
38
+ protobuf>=6.32.0
39
+ pyasn1>=0.6.1
40
+ pyasn1_modules>=0.4.2
41
+ pycparser>=2.22
42
+ pycryptodome>=3.23.0
43
+ pydantic>=2.11.7
44
+ pydantic_core>=2.33.2
45
+ PyYAML>=6.0.2
46
+ requests>=2.32.5
47
+ rsa>=4.9.1
48
+ sniffio>=1.3.1
49
+ starlette>=0.47.3
50
+ typing-inspection>=0.4.1
51
+ typing_extensions>=4.15.0
52
+ urllib3>=2.5.0
53
+ virtualenv>=20.34.0
54
+ zipp>=3.23.0
@@ -0,0 +1,79 @@
1
+ [build-system]
2
+ requires = ["setuptools", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "maleo-logging"
7
+ version = "0.0.1"
8
+ description = "Logging package for MaleoSuite"
9
+ authors = [
10
+ { name = "Agra Bima Yuda", email = "agra@nexmedis.com" }
11
+ ]
12
+ license = { text = "Proprietary"}
13
+ readme = "README.md"
14
+ requires-python = ">=3.12"
15
+ dependencies = [
16
+ "annotated-types>=0.7.0",
17
+ "anyio>=4.10.0",
18
+ "black>=25.1.0",
19
+ "cachetools>=5.5.2",
20
+ "certifi>=2025.8.3",
21
+ "cffi>=1.17.1",
22
+ "cfgv>=3.4.0",
23
+ "charset-normalizer>=3.4.3",
24
+ "click>=8.2.1",
25
+ "cryptography>=45.0.6",
26
+ "distlib>=0.4.0",
27
+ "fastapi>=0.116.1",
28
+ "filelock>=3.19.1",
29
+ "google-api-core>=2.25.1",
30
+ "google-auth>=2.40.3",
31
+ "google-cloud-appengine-logging>=1.6.2",
32
+ "google-cloud-audit-log>=0.3.2",
33
+ "google-cloud-core>=2.4.3",
34
+ "google-cloud-logging>=3.12.1",
35
+ "googleapis-common-protos>=1.70.0",
36
+ "grpc-google-iam-v1>=0.14.2",
37
+ "grpcio>=1.74.0",
38
+ "grpcio-status>=1.74.0",
39
+ "identify>=2.6.13",
40
+ "idna>=3.10",
41
+ "importlib_metadata>=8.7.0",
42
+ "maleo-enums>=0.0.2",
43
+ "maleo-types-base>=0.0.2",
44
+ "maleo-utils>=0.0.3",
45
+ "mypy_extensions>=1.1.0",
46
+ "nodeenv>=1.9.1",
47
+ "opentelemetry-api>=1.36.0",
48
+ "packaging>=25.0",
49
+ "pathspec>=0.12.1",
50
+ "platformdirs>=4.4.0",
51
+ "pre_commit>=4.3.0",
52
+ "proto-plus>=1.26.1",
53
+ "protobuf>=6.32.0",
54
+ "pyasn1>=0.6.1",
55
+ "pyasn1_modules>=0.4.2",
56
+ "pycparser>=2.22",
57
+ "pycryptodome>=3.23.0",
58
+ "pydantic>=2.11.7",
59
+ "pydantic_core>=2.33.2",
60
+ "PyYAML>=6.0.2",
61
+ "requests>=2.32.5",
62
+ "rsa>=4.9.1",
63
+ "sniffio>=1.3.1",
64
+ "starlette>=0.47.3",
65
+ "typing-inspection>=0.4.1",
66
+ "typing_extensions>=4.15.0",
67
+ "urllib3>=2.5.0",
68
+ "virtualenv>=20.34.0",
69
+ "zipp>=3.23.0"
70
+ ]
71
+
72
+ [tool.setuptools]
73
+ packages = ["maleo.logging"]
74
+
75
+ [tool.setuptools.package-data]
76
+ "maleo.logging" = ["*.json", "*.yaml"]
77
+
78
+ [tool.setuptools.package-dir]
79
+ "maleo.logging" = "src"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
File without changes
@@ -0,0 +1,24 @@
1
+ from pydantic import BaseModel, Field, ConfigDict
2
+ from typing import Optional
3
+ from maleo.enums.environment import Environment
4
+ from maleo.enums.service import Key
5
+ from maleo.types.base.string import OptionalString
6
+ from .enums import Level, LoggerType
7
+ from .google import GoogleCloudLogging
8
+
9
+
10
+ class Labels(BaseModel):
11
+ logger_type: LoggerType = Field(..., description="Logger's type")
12
+ service_environment: Environment = Field(..., description="Service's environment")
13
+ service_key: Key = Field(..., description="Service's key")
14
+ client_key: OptionalString = Field(None, description="Client's key (Optional)")
15
+
16
+
17
+ class SimpleConfig(BaseModel):
18
+ model_config = ConfigDict(arbitrary_types_allowed=True)
19
+
20
+ dir: str = Field(..., description="Log's directory")
21
+ level: Level = Field(Level.INFO, description="Log's level")
22
+ google_cloud_logging: Optional[GoogleCloudLogging] = Field(
23
+ default_factory=GoogleCloudLogging, description="Google cloud logging"
24
+ )
@@ -0,0 +1,25 @@
1
+ import logging
2
+ from enum import IntEnum, StrEnum
3
+
4
+
5
+ class Level(IntEnum):
6
+ CRITICAL = logging.CRITICAL
7
+ FATAL = logging.FATAL
8
+ ERROR = logging.ERROR
9
+ WARNING = logging.WARNING
10
+ WARN = logging.WARN
11
+ INFO = logging.INFO
12
+ DEBUG = logging.DEBUG
13
+ NOTSET = logging.NOTSET
14
+
15
+
16
+ class LoggerType(StrEnum):
17
+ BASE = "base"
18
+ APPLICATION = "application"
19
+ CACHE = "cache"
20
+ CLIENT = "client"
21
+ CONTROLLER = "controller"
22
+ DATABASE = "database"
23
+ MIDDLEWARE = "middleware"
24
+ REPOSITORY = "repository"
25
+ SERVICE = "service"
@@ -0,0 +1,44 @@
1
+ from google.cloud.logging import Client
2
+ from google.cloud.logging.handlers import CloudLoggingHandler
3
+ from google.oauth2.service_account import Credentials
4
+ from pathlib import Path
5
+ from typing import Optional, Union
6
+ from maleo.types.base.dict import OptionalStringToStringDict
7
+ from maleo.utils.loaders.credential.google import load
8
+
9
+
10
+ class GoogleCloudLogging:
11
+ def __init__(
12
+ self,
13
+ credentials: Optional[Credentials] = None,
14
+ credentials_path: Optional[Union[Path, str]] = None,
15
+ ) -> None:
16
+ if credentials is not None and credentials_path is not None:
17
+ raise ValueError(
18
+ "Only either 'credentials' or 'credentials_path' can be passed as parameter"
19
+ )
20
+
21
+ if credentials is not None:
22
+ self._credentials = credentials
23
+ else:
24
+ self._credentials = load(credentials_path)
25
+
26
+ self._client = Client(credentials=self._credentials)
27
+ self._client.setup_logging()
28
+
29
+ @property
30
+ def credentials(self) -> Credentials:
31
+ return self._credentials
32
+
33
+ @property
34
+ def client(self) -> Client:
35
+ return self._client
36
+
37
+ def dispose(self) -> None:
38
+ if self._client is not None:
39
+ self._client.close
40
+
41
+ def create_handler(
42
+ self, name: str, labels: OptionalStringToStringDict = None
43
+ ) -> CloudLoggingHandler:
44
+ return CloudLoggingHandler(client=self._client, name=name, labels=labels)
@@ -0,0 +1,622 @@
1
+ import logging
2
+ import os
3
+ from datetime import datetime, timezone
4
+ from typing import Literal, Optional, Union, overload
5
+ from maleo.enums.environment import Environment
6
+ from maleo.enums.service import Key
7
+ from maleo.types.base.dict import OptionalStringToStringDict
8
+ from maleo.types.base.string import OptionalString
9
+ from .dtos import Labels
10
+ from maleo.utils.merger import merge_dicts
11
+ from .enums import Level, LoggerType
12
+ from .google import GoogleCloudLogging
13
+
14
+
15
+ class Base(logging.Logger):
16
+ def __init__(
17
+ self,
18
+ type: LoggerType,
19
+ dir: str,
20
+ *,
21
+ environment: Optional[Environment] = None,
22
+ service_key: Optional[Key] = None,
23
+ client_key: OptionalString = None,
24
+ level: Level = Level.INFO,
25
+ google_cloud_logging: Optional[GoogleCloudLogging] = None,
26
+ labels: OptionalStringToStringDict = None,
27
+ aggregate_file_name: OptionalString = None,
28
+ inidividual_log: bool = True,
29
+ ):
30
+ self._type = type # Declare logger type
31
+
32
+ # Ensure environment exists
33
+ actual_environment = environment or os.getenv("ENVIRONMENT")
34
+ if actual_environment is None:
35
+ raise ValueError(
36
+ "ENVIRONMENT environment variable must be set if 'environment' is set to None"
37
+ )
38
+ else:
39
+ self._environment = Environment(actual_environment)
40
+
41
+ # Ensure service_key exists
42
+ actual_service_key = service_key or os.getenv("SERVICE_KEY")
43
+ if actual_service_key is None:
44
+ raise ValueError(
45
+ "SERVICE_KEY environment variable must be set if 'service_key' is set to None"
46
+ )
47
+ else:
48
+ self._service_key = Key(actual_service_key)
49
+
50
+ self._client_key = client_key # Declare client key
51
+
52
+ # Ensure client_key is valid if logger type is a client
53
+ if self._type == LoggerType.CLIENT and self._client_key is None:
54
+ raise ValueError(
55
+ "'client_key' parameter must be provided if 'logger_type' is 'client'"
56
+ )
57
+
58
+ # Define logger name
59
+ base_name = f"{self._environment} - {self._service_key} - {self._type}"
60
+ if self._type == LoggerType.CLIENT:
61
+ self._name = f"{base_name} - {self._client_key}"
62
+ else:
63
+ self._name = base_name
64
+
65
+ # Define log labels
66
+ self._labels = Labels(
67
+ logger_type=self._type,
68
+ service_environment=self._environment,
69
+ service_key=self._service_key,
70
+ client_key=client_key,
71
+ )
72
+
73
+ super().__init__(self._name, level) # Init the superclass's logger
74
+
75
+ # Clear existing handlers to prevent duplicates
76
+ for handler in list(self.handlers):
77
+ self.removeHandler(handler)
78
+ handler.close()
79
+
80
+ # Console handler
81
+ console_handler = logging.StreamHandler()
82
+ console_handler.setFormatter(
83
+ logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
84
+ )
85
+ self.addHandler(console_handler)
86
+
87
+ # Google Cloud Logging handler (If enabled)
88
+ if google_cloud_logging is not None:
89
+ final_labels = self._labels.model_dump(mode="json", exclude_none=True)
90
+ if labels is not None:
91
+ final_labels = merge_dicts(final_labels, labels)
92
+ cloud_logging_handler = google_cloud_logging.create_handler(
93
+ name=self._name.replace(" ", ""),
94
+ labels=final_labels,
95
+ )
96
+ self.addHandler(cloud_logging_handler)
97
+ else:
98
+ self.warning(
99
+ "Cloud logging is not configured. Will not add cloud logging handler"
100
+ )
101
+
102
+ # Define aggregate log directory
103
+ if aggregate_file_name is not None:
104
+ if not aggregate_file_name.endswith(".log"):
105
+ aggregate_file_name += ".log"
106
+ log_filename = os.path.join(self._log_dir, "aggregate", aggregate_file_name)
107
+
108
+ # File handler
109
+ file_handler = logging.FileHandler(log_filename, mode="a")
110
+ file_handler.setFormatter(
111
+ logging.Formatter(
112
+ "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
113
+ )
114
+ )
115
+ self.addHandler(file_handler)
116
+
117
+ if inidividual_log:
118
+ # Define log directory
119
+ if self._type == LoggerType.CLIENT:
120
+ log_dir = f"{self._type}/{self._client_key}"
121
+ else:
122
+ log_dir = f"{self._type}"
123
+ self._log_dir = os.path.join(dir, log_dir)
124
+ os.makedirs(self._log_dir, exist_ok=True)
125
+
126
+ # Generate timestamped filename
127
+ log_filename = os.path.join(
128
+ self._log_dir,
129
+ f"{datetime.now(tz=timezone.utc).isoformat(timespec="seconds")}.log",
130
+ )
131
+
132
+ # File handler
133
+ file_handler = logging.FileHandler(log_filename, mode="a")
134
+ file_handler.setFormatter(
135
+ logging.Formatter(
136
+ "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
137
+ )
138
+ )
139
+ self.addHandler(file_handler)
140
+
141
+ @property
142
+ def type(self) -> str:
143
+ return self._type
144
+
145
+ @property
146
+ def location(self) -> str:
147
+ return self._log_dir
148
+
149
+ @property
150
+ def environment(self) -> Environment:
151
+ return self._environment
152
+
153
+ @property
154
+ def service(self) -> str:
155
+ return self._service_key
156
+
157
+ @property
158
+ def client(self) -> OptionalString:
159
+ return self._client_key
160
+
161
+ @property
162
+ def identity(self) -> str:
163
+ return self._name
164
+
165
+ @property
166
+ def labels(self) -> Labels:
167
+ return self._labels
168
+
169
+ def dispose(self):
170
+ """Dispose of the logger by removing all handlers."""
171
+ for handler in list(self.handlers):
172
+ self.removeHandler(handler)
173
+ handler.close()
174
+ self.handlers.clear()
175
+
176
+
177
+ class Application(Base):
178
+ def __init__(
179
+ self,
180
+ dir: str,
181
+ environment: Optional[Environment] = None,
182
+ service_key: Optional[Key] = None,
183
+ level: Level = Level.INFO,
184
+ google_cloud_logging: Optional[GoogleCloudLogging] = None,
185
+ labels: OptionalStringToStringDict = None,
186
+ aggregate_file_name: OptionalString = None,
187
+ inidividual_log: bool = True,
188
+ ):
189
+ super().__init__(
190
+ dir=dir,
191
+ type=LoggerType.APPLICATION,
192
+ environment=environment,
193
+ service_key=service_key,
194
+ client_key=None,
195
+ level=level,
196
+ google_cloud_logging=google_cloud_logging,
197
+ labels=labels,
198
+ aggregate_file_name=aggregate_file_name,
199
+ inidividual_log=inidividual_log,
200
+ )
201
+
202
+
203
+ class Cache(Base):
204
+ def __init__(
205
+ self,
206
+ dir: str,
207
+ environment: Optional[Environment] = None,
208
+ service_key: Optional[Key] = None,
209
+ level: Level = Level.INFO,
210
+ google_cloud_logging: Optional[GoogleCloudLogging] = None,
211
+ labels: OptionalStringToStringDict = None,
212
+ aggregate_file_name: OptionalString = None,
213
+ inidividual_log: bool = True,
214
+ ):
215
+ super().__init__(
216
+ dir=dir,
217
+ type=LoggerType.CACHE,
218
+ environment=environment,
219
+ service_key=service_key,
220
+ client_key=None,
221
+ level=level,
222
+ google_cloud_logging=google_cloud_logging,
223
+ labels=labels,
224
+ aggregate_file_name=aggregate_file_name,
225
+ inidividual_log=inidividual_log,
226
+ )
227
+
228
+
229
+ class Client(Base):
230
+ def __init__(
231
+ self,
232
+ dir: str,
233
+ client_key: str,
234
+ environment: Optional[Environment] = None,
235
+ service_key: Optional[Key] = None,
236
+ level: Level = Level.INFO,
237
+ google_cloud_logging: Optional[GoogleCloudLogging] = None,
238
+ labels: OptionalStringToStringDict = None,
239
+ aggregate_file_name: OptionalString = None,
240
+ inidividual_log: bool = True,
241
+ ):
242
+ super().__init__(
243
+ dir=dir,
244
+ type=LoggerType.CLIENT,
245
+ environment=environment,
246
+ service_key=service_key,
247
+ client_key=client_key,
248
+ level=level,
249
+ google_cloud_logging=google_cloud_logging,
250
+ labels=labels,
251
+ aggregate_file_name=aggregate_file_name,
252
+ inidividual_log=inidividual_log,
253
+ )
254
+
255
+
256
+ class Controller(Base):
257
+ def __init__(
258
+ self,
259
+ dir: str,
260
+ environment: Optional[Environment] = None,
261
+ service_key: Optional[Key] = None,
262
+ level: Level = Level.INFO,
263
+ google_cloud_logging: Optional[GoogleCloudLogging] = None,
264
+ labels: OptionalStringToStringDict = None,
265
+ aggregate_file_name: OptionalString = None,
266
+ inidividual_log: bool = True,
267
+ ):
268
+ super().__init__(
269
+ dir=dir,
270
+ type=LoggerType.CONTROLLER,
271
+ environment=environment,
272
+ service_key=service_key,
273
+ client_key=None,
274
+ level=level,
275
+ google_cloud_logging=google_cloud_logging,
276
+ labels=labels,
277
+ aggregate_file_name=aggregate_file_name,
278
+ inidividual_log=inidividual_log,
279
+ )
280
+
281
+
282
+ class Database(Base):
283
+ def __init__(
284
+ self,
285
+ dir: str,
286
+ environment: Optional[Environment] = None,
287
+ service_key: Optional[Key] = None,
288
+ level=Level.INFO,
289
+ google_cloud_logging=None,
290
+ labels: OptionalStringToStringDict = None,
291
+ aggregate_file_name: OptionalString = None,
292
+ inidividual_log: bool = True,
293
+ ):
294
+ super().__init__(
295
+ dir=dir,
296
+ type=LoggerType.DATABASE,
297
+ environment=environment,
298
+ service_key=service_key,
299
+ client_key=None,
300
+ level=level,
301
+ google_cloud_logging=google_cloud_logging,
302
+ labels=labels,
303
+ aggregate_file_name=aggregate_file_name,
304
+ inidividual_log=inidividual_log,
305
+ )
306
+
307
+
308
+ class Middleware(Base):
309
+ def __init__(
310
+ self,
311
+ dir: str,
312
+ environment: Optional[Environment] = None,
313
+ service_key: Optional[Key] = None,
314
+ level=Level.INFO,
315
+ google_cloud_logging=None,
316
+ labels: OptionalStringToStringDict = None,
317
+ aggregate_file_name: OptionalString = None,
318
+ inidividual_log: bool = True,
319
+ ):
320
+ super().__init__(
321
+ dir=dir,
322
+ type=LoggerType.MIDDLEWARE,
323
+ environment=environment,
324
+ service_key=service_key,
325
+ client_key=None,
326
+ level=level,
327
+ google_cloud_logging=google_cloud_logging,
328
+ labels=labels,
329
+ aggregate_file_name=aggregate_file_name,
330
+ inidividual_log=inidividual_log,
331
+ )
332
+
333
+
334
+ class Repository(Base):
335
+ def __init__(
336
+ self,
337
+ dir: str,
338
+ environment: Optional[Environment] = None,
339
+ service_key: Optional[Key] = None,
340
+ level: Level = Level.INFO,
341
+ google_cloud_logging: Optional[GoogleCloudLogging] = None,
342
+ labels: OptionalStringToStringDict = None,
343
+ aggregate_file_name: OptionalString = None,
344
+ inidividual_log: bool = True,
345
+ ):
346
+ super().__init__(
347
+ dir=dir,
348
+ type=LoggerType.REPOSITORY,
349
+ environment=environment,
350
+ service_key=service_key,
351
+ client_key=None,
352
+ level=level,
353
+ google_cloud_logging=google_cloud_logging,
354
+ labels=labels,
355
+ aggregate_file_name=aggregate_file_name,
356
+ inidividual_log=inidividual_log,
357
+ )
358
+
359
+
360
+ class Service(Base):
361
+ def __init__(
362
+ self,
363
+ dir: str,
364
+ environment: Optional[Environment] = None,
365
+ service_key: Optional[Key] = None,
366
+ level: Level = Level.INFO,
367
+ google_cloud_logging: Optional[GoogleCloudLogging] = None,
368
+ labels: OptionalStringToStringDict = None,
369
+ aggregate_file_name: OptionalString = None,
370
+ inidividual_log: bool = True,
371
+ ):
372
+ super().__init__(
373
+ dir=dir,
374
+ type=LoggerType.SERVICE,
375
+ environment=environment,
376
+ service_key=service_key,
377
+ client_key=None,
378
+ level=level,
379
+ google_cloud_logging=google_cloud_logging,
380
+ labels=labels,
381
+ aggregate_file_name=aggregate_file_name,
382
+ inidividual_log=inidividual_log,
383
+ )
384
+
385
+
386
+ @overload
387
+ def create(
388
+ dir: str,
389
+ type: Literal[LoggerType.APPLICATION],
390
+ *,
391
+ environment: Optional[Environment] = None,
392
+ service_key: Optional[Key] = None,
393
+ level: Level = Level.INFO,
394
+ google_cloud_logging: Optional[GoogleCloudLogging] = None,
395
+ labels: OptionalStringToStringDict = None,
396
+ aggregate_file_name: OptionalString = None,
397
+ inidividual_log: bool = True,
398
+ ) -> Application: ...
399
+ @overload
400
+ def create(
401
+ dir: str,
402
+ type: Literal[LoggerType.CACHE],
403
+ *,
404
+ environment: Optional[Environment] = None,
405
+ service_key: Optional[Key] = None,
406
+ level: Level = Level.INFO,
407
+ google_cloud_logging: Optional[GoogleCloudLogging] = None,
408
+ labels: OptionalStringToStringDict = None,
409
+ aggregate_file_name: OptionalString = None,
410
+ inidividual_log: bool = True,
411
+ ) -> Cache: ...
412
+ @overload
413
+ def create(
414
+ dir: str,
415
+ type: Literal[LoggerType.CLIENT],
416
+ *,
417
+ environment: Optional[Environment] = None,
418
+ service_key: Optional[Key] = None,
419
+ client_key: str,
420
+ level: Level = Level.INFO,
421
+ google_cloud_logging: Optional[GoogleCloudLogging] = None,
422
+ labels: OptionalStringToStringDict = None,
423
+ aggregate_file_name: OptionalString = None,
424
+ inidividual_log: bool = True,
425
+ ) -> Client: ...
426
+ @overload
427
+ def create(
428
+ dir: str,
429
+ type: Literal[LoggerType.CONTROLLER],
430
+ *,
431
+ environment: Optional[Environment] = None,
432
+ service_key: Optional[Key] = None,
433
+ level: Level = Level.INFO,
434
+ google_cloud_logging: Optional[GoogleCloudLogging] = None,
435
+ labels: OptionalStringToStringDict = None,
436
+ aggregate_file_name: OptionalString = None,
437
+ inidividual_log: bool = True,
438
+ ) -> Controller: ...
439
+ @overload
440
+ def create(
441
+ dir: str,
442
+ type: Literal[LoggerType.DATABASE],
443
+ *,
444
+ environment: Optional[Environment] = None,
445
+ service_key: Optional[Key] = None,
446
+ level: Level = Level.INFO,
447
+ google_cloud_logging: Optional[GoogleCloudLogging] = None,
448
+ labels: OptionalStringToStringDict = None,
449
+ aggregate_file_name: OptionalString = None,
450
+ inidividual_log: bool = True,
451
+ ) -> Database: ...
452
+ @overload
453
+ def create(
454
+ dir: str,
455
+ type: Literal[LoggerType.MIDDLEWARE],
456
+ *,
457
+ environment: Optional[Environment] = None,
458
+ service_key: Optional[Key] = None,
459
+ level: Level = Level.INFO,
460
+ google_cloud_logging: Optional[GoogleCloudLogging] = None,
461
+ labels: OptionalStringToStringDict = None,
462
+ aggregate_file_name: OptionalString = None,
463
+ inidividual_log: bool = True,
464
+ ) -> Middleware: ...
465
+ @overload
466
+ def create(
467
+ dir: str,
468
+ type: Literal[LoggerType.REPOSITORY],
469
+ *,
470
+ environment: Optional[Environment] = None,
471
+ service_key: Optional[Key] = None,
472
+ level: Level = Level.INFO,
473
+ google_cloud_logging: Optional[GoogleCloudLogging] = None,
474
+ labels: OptionalStringToStringDict = None,
475
+ aggregate_file_name: OptionalString = None,
476
+ inidividual_log: bool = True,
477
+ ) -> Repository: ...
478
+ @overload
479
+ def create(
480
+ dir: str,
481
+ type: Literal[LoggerType.SERVICE],
482
+ *,
483
+ environment: Optional[Environment] = None,
484
+ service_key: Optional[Key] = None,
485
+ level: Level = Level.INFO,
486
+ google_cloud_logging: Optional[GoogleCloudLogging] = None,
487
+ labels: OptionalStringToStringDict = None,
488
+ aggregate_file_name: OptionalString = None,
489
+ inidividual_log: bool = True,
490
+ ) -> Service: ...
491
+ def create(
492
+ dir: str,
493
+ type: LoggerType = LoggerType.BASE,
494
+ *,
495
+ environment: Optional[Environment] = None,
496
+ service_key: Optional[Key] = None,
497
+ client_key: OptionalString = None,
498
+ level: Level = Level.INFO,
499
+ google_cloud_logging: Optional[GoogleCloudLogging] = None,
500
+ labels: OptionalStringToStringDict = None,
501
+ aggregate_file_name: OptionalString = None,
502
+ inidividual_log: bool = True,
503
+ ) -> Union[
504
+ Base,
505
+ Application,
506
+ Cache,
507
+ Client,
508
+ Controller,
509
+ Database,
510
+ Middleware,
511
+ Repository,
512
+ Service,
513
+ ]:
514
+ if type not in LoggerType:
515
+ raise ValueError(f"Invalid logger type of '{type}'")
516
+
517
+ if type is LoggerType.BASE:
518
+ return Base(
519
+ type=type,
520
+ dir=dir,
521
+ environment=environment,
522
+ service_key=service_key,
523
+ client_key=client_key,
524
+ level=level,
525
+ google_cloud_logging=google_cloud_logging,
526
+ labels=labels,
527
+ aggregate_file_name=aggregate_file_name,
528
+ inidividual_log=inidividual_log,
529
+ )
530
+ elif type is LoggerType.APPLICATION:
531
+ return Application(
532
+ dir=dir,
533
+ environment=environment,
534
+ service_key=service_key,
535
+ level=level,
536
+ google_cloud_logging=google_cloud_logging,
537
+ labels=labels,
538
+ aggregate_file_name=aggregate_file_name,
539
+ inidividual_log=inidividual_log,
540
+ )
541
+ elif type is LoggerType.CACHE:
542
+ return Cache(
543
+ dir=dir,
544
+ environment=environment,
545
+ service_key=service_key,
546
+ level=level,
547
+ google_cloud_logging=google_cloud_logging,
548
+ labels=labels,
549
+ aggregate_file_name=aggregate_file_name,
550
+ inidividual_log=inidividual_log,
551
+ )
552
+ elif type is LoggerType.CLIENT:
553
+ if client_key is None:
554
+ raise ValueError(
555
+ "Argument 'client_key' can not be None if 'logger_type' is 'client'"
556
+ )
557
+ return Client(
558
+ dir=dir,
559
+ client_key=client_key,
560
+ environment=environment,
561
+ service_key=service_key,
562
+ level=level,
563
+ google_cloud_logging=google_cloud_logging,
564
+ labels=labels,
565
+ aggregate_file_name=aggregate_file_name,
566
+ inidividual_log=inidividual_log,
567
+ )
568
+ elif type is LoggerType.CONTROLLER:
569
+ return Controller(
570
+ dir=dir,
571
+ environment=environment,
572
+ service_key=service_key,
573
+ level=level,
574
+ google_cloud_logging=google_cloud_logging,
575
+ labels=labels,
576
+ aggregate_file_name=aggregate_file_name,
577
+ inidividual_log=inidividual_log,
578
+ )
579
+ elif type is LoggerType.DATABASE:
580
+ return Database(
581
+ dir=dir,
582
+ environment=environment,
583
+ service_key=service_key,
584
+ level=level,
585
+ google_cloud_logging=google_cloud_logging,
586
+ labels=labels,
587
+ aggregate_file_name=aggregate_file_name,
588
+ inidividual_log=inidividual_log,
589
+ )
590
+ elif type is LoggerType.MIDDLEWARE:
591
+ return Middleware(
592
+ dir=dir,
593
+ environment=environment,
594
+ service_key=service_key,
595
+ level=level,
596
+ google_cloud_logging=google_cloud_logging,
597
+ labels=labels,
598
+ aggregate_file_name=aggregate_file_name,
599
+ inidividual_log=inidividual_log,
600
+ )
601
+ elif type is LoggerType.REPOSITORY:
602
+ return Repository(
603
+ dir=dir,
604
+ environment=environment,
605
+ service_key=service_key,
606
+ level=level,
607
+ google_cloud_logging=google_cloud_logging,
608
+ labels=labels,
609
+ aggregate_file_name=aggregate_file_name,
610
+ inidividual_log=inidividual_log,
611
+ )
612
+ elif type is LoggerType.SERVICE:
613
+ return Service(
614
+ dir=dir,
615
+ environment=environment,
616
+ service_key=service_key,
617
+ level=level,
618
+ google_cloud_logging=google_cloud_logging,
619
+ labels=labels,
620
+ aggregate_file_name=aggregate_file_name,
621
+ inidividual_log=inidividual_log,
622
+ )