Skip to content

Commit ffd706a

Browse files
committed
refactor: support for logger transport(s), update README
1 parent 07e95e6 commit ffd706a

File tree

131 files changed

+1200
-724
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

131 files changed

+1200
-724
lines changed

CHANGELOG.md

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,68 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7+
## [1.8.0] - 2025-04-22
8+
9+
### Added
10+
11+
- Added support to add single `transport` or multiple transport using key `transports`. The transport parameter allows you to implement custom logging behavior by providing your own logging functions.
12+
13+
```python
14+
from vwo import init
15+
16+
class CustomTransport:
17+
def __init__(self, config):
18+
self.level = config.get('level', "ERROR")
19+
self.config = config
20+
21+
def log(self, level, message):
22+
# your custom implementation here
23+
24+
options = {
25+
'sdk_key': '32-alpha-numeric-sdk-key', # SDK Key
26+
'account_id': '123456', # VWO Account ID
27+
'logger' {
28+
'transport': CustomTransport({'level': 'INFO'})
29+
}
30+
}
31+
32+
vwo_client = init(options)
33+
```
34+
35+
- For multiple transports you can use the `transports` parameter. For example:
36+
```python
37+
from vwo import init
38+
39+
class CustomTransportForInfo:
40+
def __init__(self, config):
41+
self.level = config.get('level', "INFO")
42+
self.config = config
43+
44+
def log(self, level, message):
45+
# your custom implementation here
46+
47+
class CustomTransportForError:
48+
def __init__(self, config):
49+
self.level = config.get('level', "ERROR")
50+
self.config = config
51+
52+
def log(self, level, message):
53+
# your custom implementation here
54+
55+
options = {
56+
'sdk_key': '32-alpha-numeric-sdk-key', # SDK Key
57+
'account_id': '123456', # VWO Account ID
58+
'logger' {
59+
'transports': [
60+
CustomTransportForInfo({'level': 'INFO'}),
61+
CustomTransportForError({'level': 'ERROR'})
62+
]
63+
}
64+
}
65+
66+
vwo_client = init(options)
67+
```
68+
769
## [1.7.0] - 2025-04-11
870

971
### Added

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@
187187
same "printed page" as the copyright notice for easier
188188
identification within third-party archives.
189189

190-
Copyright 2024 Wingify Software Pvt. Ltd.
190+
Copyright 2024-2025 Wingify Software Pvt. Ltd.
191191

192192
Licensed under the Apache License, Version 2.0 (the "License");
193193
you may not use this file except in compliance with the License.

NOTICE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Copyright 2024 Wingify Software Pvt. Ltd.
1+
Copyright 2024-2025 Wingify Software Pvt. Ltd.
22

33
Licensed under the Apache License, Version 2.0 (the "License");
44
you may not use this file except in compliance with the License.

README.md

Lines changed: 81 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,24 @@ options = {
240240
vwo_client = init(options)
241241
```
242242

243+
### Integrations
244+
VWO FME SDKs provide seamless integration with third-party tools like analytics platforms, monitoring services, customer data platforms (CDPs), and messaging systems. This is achieved through a simple yet powerful callback mechanism that receives VWO-specific properties and can forward them to any third-party tool of your choice.
245+
246+
```python
247+
def callback(properties):
248+
# properties will contain all the required VWO specific information
249+
print(properties)
250+
251+
options = {
252+
'sdk_key': '32-alpha-numeric-sdk-key', # SDK Key
253+
'account_id': '12345', # VWO Account ID
254+
'integrations': {
255+
'callback': callback
256+
}
257+
}
258+
vwo_client = init(options)
259+
```
260+
243261
### Logger
244262

245263
VWO by default logs all `ERROR` level messages to your server console.
@@ -249,6 +267,7 @@ To gain more control over VWO's logging behaviour, you can use the `logger` para
249267
| ------------- | -------------------------------------- | ------------ | -------- | ----------------- |
250268
| `level` | Log level to control verbosity of logs | Yes | str | `ERROR` |
251269
| `prefix` | Custom prefix for log messages | No | str | `VWO-SDK` |
270+
| `transport` | Custom logger implementation | No | object | See example below |
252271

253272
#### Example 1: Set log level to control verbosity of logs
254273

@@ -277,21 +296,74 @@ options = {
277296
vwo_client = init(options)
278297
```
279298

280-
### Integrations
281-
VWO FME SDKs provide seamless integration with third-party tools like analytics platforms, monitoring services, customer data platforms (CDPs), and messaging systems. This is achieved through a simple yet powerful callback mechanism that receives VWO-specific properties and can forward them to any third-party tool of your choice.
299+
#### Example 3: Implement custom transport to handle logs your way
282300

301+
The `transport` parameter allows you to implement custom logging behavior by providing your own logging functions. You can define handlers for different log levels (`debug`, `info`, `warn`, `error`, `trace`) to process log messages according to your needs.
302+
303+
For example, you could:
304+
305+
- Send logs to a third-party logging service
306+
- Write logs to a file
307+
- Format log messages differently
308+
- Filter or transform log messages
309+
- Route different log levels to different destinations
310+
311+
The transport object should implement handlers for the log levels you want to customize. Each handler receives the log message as a parameter.
312+
313+
For single transport you can use the `transport` parameter. For example:
283314
```python
284-
def callback(properties):
285-
# properties will contain all the required VWO specific information
286-
print(properties)
315+
from vwo import init
316+
317+
class CustomTransport:
318+
def __init__(self, config):
319+
self.level = config.get('level', "ERROR")
320+
self.config = config
321+
322+
def log(self, level, message):
323+
# your custom implementation here
287324

288325
options = {
289326
'sdk_key': '32-alpha-numeric-sdk-key', # SDK Key
290-
'account_id': '12345', # VWO Account ID
291-
'integrations': {
292-
'callback': callback
327+
'account_id': '123456', # VWO Account ID
328+
'logger' {
329+
'transport': CustomTransport({'level': 'INFO'})
293330
}
294331
}
332+
333+
vwo_client = init(options)
334+
```
335+
336+
For multiple transports you can use the `transports` parameter. For example:
337+
```python
338+
from vwo import init
339+
340+
class CustomTransportForInfo:
341+
def __init__(self, config):
342+
self.level = config.get('level', "INFO")
343+
self.config = config
344+
345+
def log(self, level, message):
346+
# your custom implementation here
347+
348+
class CustomTransportForError:
349+
def __init__(self, config):
350+
self.level = config.get('level', "ERROR")
351+
self.config = config
352+
353+
def log(self, level, message):
354+
# your custom implementation here
355+
356+
options = {
357+
'sdk_key': '32-alpha-numeric-sdk-key', # SDK Key
358+
'account_id': '123456', # VWO Account ID
359+
'logger' {
360+
'transports': [
361+
CustomTransportForInfo({'level': 'INFO'}),
362+
CustomTransportForError({'level': 'ERROR'})
363+
]
364+
}
365+
}
366+
295367
vwo_client = init(options)
296368
```
297369

@@ -328,4 +400,4 @@ Our [Code of Conduct](https://github.com/wingify/vwo-fme-python-sdk/blob/master/
328400

329401
[Apache License, Version 2.0](https://github.com/wingify/vwo-fme-python-sdk/blob/master/LICENSE)
330402

331-
Copyright 2024 Wingify Software Pvt. Ltd.
403+
Copyright 2024-2025 Wingify Software Pvt. Ltd.

scripts/apache_license_check.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright 2024 Wingify Software Pvt. Ltd.
1+
# Copyright 2024-2025 Wingify Software Pvt. Ltd.
22
#
33
# Licensed under the Apache License, Version 2.0 (the "License");
44
# you may not use this file except in compliance with the License.

scripts/doc_check.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright 2024 Wingify Software Pvt. Ltd.
1+
# Copyright 2024-2025 Wingify Software Pvt. Ltd.
22
#
33
# Licensed under the Apache License, Version 2.0 (the "License");
44
# you may not use this file except in compliance with the License.
@@ -29,7 +29,7 @@ def is_constant_class(Foo):
2929

3030
# Reference taken from https://stackoverflow.com/a/25562415/13268491
3131
def import_submodules(package, recursive=True):
32-
""" Import all submodules of a module, recursively, including subpackages
32+
"""Import all submodules of a module, recursively, including subpackages
3333
3434
:param package: package (name or actual module)
3535
:type package: str | module
@@ -60,7 +60,12 @@ def find_non_doc_code(obj):
6060
else:
6161
find_non_doc_code(value)
6262
if not value.__doc__:
63-
print('"' + inspect.getsourcefile(value) + '", line ' + str(inspect.getsourcelines(value)[-1]))
63+
print(
64+
'"'
65+
+ inspect.getsourcefile(value)
66+
+ '", line '
67+
+ str(inspect.getsourcelines(value)[-1])
68+
)
6469
is_document_missing = True
6570
except Exception:
6671
pass
@@ -78,4 +83,4 @@ def find_non_doc_code(obj):
7883
is_document_missing = False
7984
for module in modules:
8085
is_document_missing |= find_non_doc_code(module)
81-
sys.exit(is_document_missing)
86+
sys.exit(is_document_missing)

setup.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright 2024 Wingify Software Pvt. Ltd.
1+
# Copyright 2024-2025 Wingify Software Pvt. Ltd.
22
#
33
# Licensed under the Apache License, Version 2.0 (the "License");
44
# you may not use this file except in compliance with the License.
@@ -57,7 +57,7 @@ def finalize_options(self):
5757

5858
def run(self):
5959
subprocess.call(
60-
'python3 ./scripts/apache_license_check.py vwo/ tests/ setup.py --copyright 2024 Wingify Software Pvt. Ltd."',
60+
'python3 ./scripts/apache_license_check.py vwo/ tests/ setup.py --Copyright 2024-2025 Wingify Software Pvt. Ltd."',
6161
shell=True,
6262
)
6363

@@ -121,7 +121,7 @@ def run(self):
121121

122122
setup(
123123
name="vwo-fme-python-sdk",
124-
version="1.7.0",
124+
version="1.8.0",
125125
description="VWO Feature Management and Experimentation SDK for Python",
126126
long_description=long_description,
127127
long_description_content_type="text/markdown",

tests/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright 2024 Wingify Software Pvt. Ltd.
1+
# Copyright 2024-2025 Wingify Software Pvt. Ltd.
22
#
33
# Licensed under the Apache License, Version 2.0 (the "License");
44
# you may not use this file except in compliance with the License.
@@ -10,4 +10,4 @@
1010
# distributed under the License is distributed on an "AS IS" BASIS,
1111
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212
# See the License for the specific language governing permissions and
13-
# limitations under the License.
13+
# limitations under the License.

tests/data/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright 2024 Wingify Software Pvt. Ltd.
1+
# Copyright 2024-2025 Wingify Software Pvt. Ltd.
22
#
33
# Licensed under the Apache License, Version 2.0 (the "License");
44
# you may not use this file except in compliance with the License.
@@ -10,4 +10,4 @@
1010
# distributed under the License is distributed on an "AS IS" BASIS,
1111
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212
# See the License for the specific language governing permissions and
13-
# limitations under the License.
13+
# limitations under the License.

tests/data/dummy_test_data_reader.py

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright 2024 Wingify Software Pvt. Ltd.
1+
# Copyright 2024-2025 Wingify Software Pvt. Ltd.
22
#
33
# Licensed under the Apache License, Version 2.0 (the "License");
44
# you may not use this file except in compliance with the License.
@@ -19,6 +19,7 @@
1919
# Determine the base directory (the directory containing this script)
2020
base_dir = os.path.dirname(__file__)
2121

22+
2223
def load_settings_json_files(directory_path):
2324
"""
2425
Load all JSON files from a specified directory into a dictionary.
@@ -30,32 +31,33 @@ def load_settings_json_files(directory_path):
3031

3132
# Iterate through all files in the directory
3233
for filename in os.listdir(directory_path):
33-
if filename.endswith('.json'):
34+
if filename.endswith(".json"):
3435
file_path = os.path.join(directory_path, filename)
3536
try:
36-
with open(file_path, 'r', encoding='utf-8') as file:
37+
with open(file_path, "r", encoding="utf-8") as file:
3738
json_content = json.load(file)
3839
# remove .json from filename
39-
filename = filename.split('.')[0]
40+
filename = filename.split(".")[0]
4041
json_files_content[filename] = json_content
4142
except (json.JSONDecodeError, IOError) as e:
4243
print(f"Error reading {filename}: {e}")
4344

4445
return json_files_content
4546

47+
4648
def load_test_data_json_file(filename: str) -> Dict[str, str]:
4749
"""
4850
Loads a JSON file and returns its content as a dictionary.
49-
51+
5052
:param filename: The name of the JSON file to load.
5153
:return: A dictionary with the contents of the JSON file.
5254
"""
53-
filepath = os.path.join(base_dir, 'test_cases', filename)
54-
with open(filepath, 'r') as file:
55+
filepath = os.path.join(base_dir, "test_cases", filename)
56+
with open(filepath, "r") as file:
5557
return json.load(file)
5658

5759

5860
# Load the JSON files
59-
settings_files = load_settings_json_files(os.path.join(base_dir, 'settings'))
60-
test_data = load_test_data_json_file('index.json')
61-
segmentor_dummy_dsl = load_test_data_json_file('dummy_dsl.json')
61+
settings_files = load_settings_json_files(os.path.join(base_dir, "settings"))
62+
test_data = load_test_data_json_file("index.json")
63+
segmentor_dummy_dsl = load_test_data_json_file("dummy_dsl.json")

0 commit comments

Comments
 (0)