Skip to content

Commit 071b3db

Browse files
Abhi591rohitesh-wingify
authored andcommitted
feat: enhanced logging capabilities
1 parent e4fc888 commit 071b3db

35 files changed

+589
-296
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ 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.14.0] - 2025-11-17
8+
9+
### Added
10+
11+
- Enhanced Logging capabilities at VWO by sending `vwo_sdkDebug` event with additional debug properties.
712

813
## [1.13.0] - 2025-09-04
914

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ def run(self):
121121

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

vwo/api/get_flag_api.py

Lines changed: 62 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,7 @@
1616
from typing import Dict, Any, List
1717
from ..models.settings.settings_model import SettingsModel
1818
from ..models.user.context_model import ContextModel
19-
from ..models.user.context_model import ContextModel
2019
from ..services.hooks_manager import HooksManager
21-
from ..packages.logger.core.log_manager import LogManager
2220
from ..enums.api_enum import ApiEnum
2321
from ..enums.campaign_type_enum import CampaignTypeEnum
2422
from ..utils.log_message_util import debug_messages, info_messages, error_messages
@@ -40,6 +38,10 @@
4038
from ..services.storage_service import StorageService
4139
from ..decorators.storage_decorator import StorageDecorator
4240
from ..utils.campaign_util import get_variation_from_campaign_key
41+
from ..enums.debug_category_enum import DebugCategoryEnum
42+
from ..packages.logger.enums.log_level_enum import LogLevelEnum
43+
from ..constants.Constants import Constants
44+
from ..utils.debugger_service_util import send_debug_event_to_vwo
4345

4446

4547
class GetFlagApi:
@@ -74,6 +76,13 @@ def get(
7476
"api": ApiEnum.GET_FLAG.value,
7577
}
7678

79+
debug_event_props = {
80+
"an": ApiEnum.GET_FLAG.value,
81+
"fk": feature_key,
82+
"uuid": context.get_vwo_uuid(),
83+
"sId": context.get_vwo_session_id(),
84+
}
85+
7786
storage_service = StorageService()
7887
stored_data = StorageDecorator().get_feature_from_storage(
7988
feature_key, context, storage_service
@@ -128,11 +137,7 @@ def get(
128137
self._passed_rules_information.update(feature_info)
129138

130139
if feature is None:
131-
LogManager.get_instance().error(
132-
error_messages.get("FEATURE_NOT_FOUND").format(
133-
featureKey=feature_key,
134-
)
135-
)
140+
LogManager.get_instance().error_log("FEATURE_NOT_FOUND", data={"featureKey": feature_key}, debug_data = debug_event_props)
136141
self._get_flag_response.set_is_enabled(False)
137142
return self._get_flag_response
138143

@@ -194,6 +199,7 @@ def get(
194199
rollout_rules_to_evaluate[0].get_id(),
195200
variation.get_id(),
196201
context,
202+
feature_key,
197203
)
198204

199205
if not roll_out_rules:
@@ -262,6 +268,7 @@ def get(
262268
experiment_rules_to_evaluate[0].get_id(),
263269
variation.get_id(),
264270
context,
271+
feature_key,
265272
)
266273

267274
if self._get_flag_response.is_enabled():
@@ -277,6 +284,16 @@ def get(
277284
hook_manager.set(decision)
278285
hook_manager.execute(hook_manager.get())
279286

287+
# send debug event, if debugger is enabled
288+
if feature.get_is_debugger_enabled():
289+
debug_event_props["cg"] = DebugCategoryEnum.DECISION.value
290+
debug_event_props["lt"] = LogLevelEnum.INFO.value
291+
debug_event_props["msg_t"] = Constants.FLAG_DECISION_GIVEN
292+
293+
# update debug event props with decision keys
294+
self.update_debug_event_props_with_message(debug_event_props, decision)
295+
send_debug_event_to_vwo(debug_event_props);
296+
280297
if (
281298
feature.get_impact_campaign()
282299
and feature.get_impact_campaign().get_campaign_id()
@@ -300,6 +317,7 @@ def get(
300317
2 if self._get_flag_response.is_enabled() else 1
301318
), # 2 is for Variation (flag enabled), 1 is for Control (flag disabled)
302319
context,
320+
feature_key,
303321
)
304322

305323
return self._get_flag_response
@@ -327,3 +345,40 @@ def _update_integrations_decision_object(
327345
}
328346
)
329347
decision.update(self._passed_rules_information)
348+
349+
def update_debug_event_props_with_message(
350+
self,
351+
debug_event_props: Dict[str, Any],
352+
decision: Dict[str, Any],
353+
) -> None:
354+
"""
355+
Updates the debug event props with the decision keys.
356+
357+
:param debug_event_props: The debug event props to update.
358+
:param decision: The decision object to extract the keys from.
359+
"""
360+
feature_key = decision.get("featureKey") or ""
361+
message = f"Flag decision given for feature:{feature_key}."
362+
363+
rollout_key = decision.get("rolloutKey")
364+
rollout_variation_id = decision.get("rolloutVariationId")
365+
if rollout_key and rollout_variation_id:
366+
prefix = f"{feature_key}_"
367+
rollout_suffix = (
368+
rollout_key[len(prefix):]
369+
if isinstance(rollout_key, str) and isinstance(feature_key, str) and rollout_key.startswith(prefix)
370+
else rollout_key
371+
)
372+
message += f" Got rollout:{rollout_suffix} with variation:{rollout_variation_id}"
373+
374+
experiment_key = decision.get("experimentKey")
375+
experiment_variation_id = decision.get("experimentVariationId")
376+
if experiment_key and experiment_variation_id:
377+
prefix = f"{feature_key}_"
378+
experiment_suffix = (
379+
experiment_key[len(prefix):]
380+
if isinstance(experiment_key, str) and isinstance(feature_key, str) and experiment_key.startswith(prefix)
381+
else experiment_key
382+
)
383+
message += f" and experiment:{experiment_suffix} with variation:{experiment_variation_id}"
384+
debug_event_props["msg"] = message

vwo/api/set_attribute_api.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,9 @@ def create_and_send_impression_for_attribute(
4444
# Construct payload data for tracking the goal
4545
payload = get_attribute_payload_data(
4646
settings,
47-
context.get_id(),
47+
context,
4848
EventEnum.VWO_SYNC_VISITOR_PROP.value,
49-
attribute_map,
50-
visitor_user_agent=context.get_user_agent(),
51-
ip_address=context.get_ip_address(),
49+
attribute_map
5250
)
5351

5452
from vwo.vwo_client import VWOClient

vwo/api/track_api.py

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -54,13 +54,11 @@ def track(
5454
self.create_and_send_impression_for_track(
5555
settings, event_name, context, event_properties
5656
)
57-
hook_manager.set({"event_name": event_name, "api": ApiEnum.TRACK.value})
57+
hook_manager.set({"event_name": event_name, "api": ApiEnum.TRACK_EVENT.value})
5858
hook_manager.execute(hook_manager.get())
5959
return {event_name: True}
6060

61-
LogManager.get_instance().error(
62-
error_messages.get("EVENT_NOT_FOUND").format(eventName=event_name)
63-
)
61+
LogManager.get_instance().error_log("EVENT_NOT_FOUND", data={"eventName": event_name}, debug_data={"an": ApiEnum.TRACK_EVENT.value, "uuid": context.get_vwo_uuid(), "sId": context.get_vwo_session_id()})
6462
return {event_name: False}
6563

6664
def create_and_send_impression_for_track(
@@ -86,11 +84,9 @@ def create_and_send_impression_for_track(
8684
# Construct payload data for tracking the goal
8785
payload = get_track_goal_payload_data(
8886
settings,
89-
context.get_id(),
87+
context,
9088
event_name,
9189
event_properties,
92-
visitor_user_agent=context.get_user_agent(),
93-
ip_address=context.get_ip_address(),
9490
)
9591

9692
from vwo.vwo_client import VWOClient

vwo/constants/Constants.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ class Constants:
1717
# TODO: read from setup.py
1818
sdk_meta = {
1919
"name": "vwo-fme-python-sdk",
20-
"version": "1.13.0"
20+
"version": "1.14.0"
2121
}
2222

2323
# Constants
@@ -63,3 +63,12 @@ class Constants:
6363
MAX_RETRIES = 3
6464
INITIAL_WAIT_TIME = 2
6565
PRODUCT_NAME = "fme"
66+
67+
# Debugger constants
68+
V2_SETTINGS = "v2-settings"
69+
POLLING = "polling"
70+
BROWSER_STORAGE = "browserStorage"
71+
FLAG_DECISION_GIVEN = "FLAG_DECISION_GIVEN"
72+
NETWORK_CALL_FAILURE_AFTER_MAX_RETRIES = "NETWORK_CALL_FAILURE_AFTER_MAX_RETRIES"
73+
NETWORK_CALL_SUCCESS_WITH_RETRIES = "NETWORK_CALL_SUCCESS_WITH_RETRIES"
74+
IMPACT_ANALYSIS = "IMPACT_ANALYSIS"

vwo/decorators/storage_decorator.py

Lines changed: 7 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from ..services.storage_service import StorageService
2121
from ..packages.logger.core.log_manager import LogManager
2222
from ..utils.log_message_util import error_messages
23+
from ..enums.api_enum import ApiEnum
2324

2425

2526
class StorageDecorator:
@@ -50,45 +51,23 @@ def set_data_in_storage(
5051
context = data.get("context")
5152

5253
if not feature_key:
53-
LogManager.get_instance().error(
54-
error_messages.get(
55-
error_messages.get("STORED_DATA_ERROR").format(key="featureKey")
56-
)
57-
)
54+
LogManager.get_instance().error_log("ERROR_STORING_DATA_IN_STORAGE",data={"key": "featureKey"}, debug_data={"an": ApiEnum.GET_FLAG.value, "uuid": context.get_vwo_uuid(), "sId": context.get_vwo_session_id()})
5855
return None # Invalid feature key, return None
5956

6057
if not context or not context.get_id():
61-
LogManager.get_instance().error(
62-
error_messages.get(
63-
error_messages.get("STORED_DATA_ERROR").format(
64-
key="Context or Context.id"
65-
)
66-
)
67-
)
58+
LogManager.get_instance().error_log("ERROR_STORING_DATA_IN_STORAGE",data={"key": "Context or Context.id"}, debug_data={"an": ApiEnum.GET_FLAG.value, "uuid": context.get_vwo_uuid(), "sId": context.get_vwo_session_id()})
6859
return None # Invalid user ID, return None
6960

7061
if (
7162
data.get("rolloutKey")
7263
and not data.get("experimentKey")
7364
and not data.get("rolloutVariationId")
7465
):
75-
LogManager.get_instance().error(
76-
error_messages.get(
77-
error_messages.get("STORED_DATA_ERROR").format(
78-
key="Context or Context.id"
79-
)
80-
)
81-
)
66+
LogManager.get_instance().error_log("ERROR_STORING_DATA_IN_STORAGE",data={"key": "rolloutKey or experimentKey or rolloutVariationId"}, debug_data={"an": ApiEnum.GET_FLAG.value, "uuid": context.get_vwo_uuid(), "sId": context.get_vwo_session_id()})
8267
return None # Invalid rollout variation, return None
8368

8469
if data.get("experimentKey") and not data.get("experimentVariationId"):
85-
LogManager.get_instance().error(
86-
error_messages.get(
87-
error_messages.get("STORED_DATA_ERROR").format(
88-
key="Variation:(rolloutKey, experimentKey or rolloutVariationId)"
89-
)
90-
)
91-
)
70+
LogManager.get_instance().error_log("ERROR_STORING_DATA_IN_STORAGE",data={"key": "Variation:(rolloutKey, experimentKey or rolloutVariationId)"}, debug_data={"an": ApiEnum.GET_FLAG.value, "uuid": context.get_vwo_uuid(), "sId": context.get_vwo_session_id()})
9271
return None # Invalid experiment variation, return None
9372

9473
success = storage_service.set_data_in_storage(
@@ -101,7 +80,8 @@ def set_data_in_storage(
10180
"experimentId": data.get("experimentId"),
10281
"experimentKey": data.get("experimentKey"),
10382
"experimentVariationId": data.get("experimentVariationId"),
104-
}
83+
},
84+
context
10585
)
10686

10787
if success:

vwo/enums/api_enum.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,9 @@
1717

1818

1919
class ApiEnum(Enum):
20+
INIT = "init"
2021
GET_FLAG = "getFlag"
21-
TRACK = "track"
22+
TRACK_EVENT = "trackEvent"
23+
SET_ATTRIBUTE = "setAttribute"
24+
UPDATE_SETTINGS = "updateSettings"
25+
FLUSH_EVENTS = "flushEvents"

vwo/enums/debug_category_enum.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Copyright 2024-2025 Wingify Software Pvt. Ltd.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
16+
from enum import Enum
17+
18+
19+
class DebugCategoryEnum(Enum):
20+
NETWORK = "network"
21+
DECISION = "decision"
22+
INITIALIZATION = "initialization"
23+
RETRY = "retry"
24+
ERROR = "error"

vwo/enums/event_enum.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,5 @@ class EventEnum(Enum):
2121
VWO_SYNC_VISITOR_PROP = "vwo_syncVisitorProp"
2222
VWO_LOG_EVENT = "vwo_log"
2323
VWO_SDK_INIT_EVENT = "vwo_fmeSdkInit"
24-
VWO_USAGE_STATS = "vwo_sdkUsageStats"
24+
VWO_USAGE_STATS = "vwo_sdkUsageStats"
25+
VWO_DEBUGGER_EVENT = "vwo_sdkDebug"

0 commit comments

Comments
 (0)