Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixing Posthog handler for thread safety #5968

Merged
merged 6 commits into from
Jan 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 26 additions & 8 deletions openbb_platform/core/openbb_core/app/command_runner.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
"""Command runner module."""
import warnings
from contextlib import nullcontext
from copy import deepcopy
Expand Down Expand Up @@ -27,6 +28,8 @@


class ExecutionContext:
"""Execution context."""

def __init__(
self,
command_map: CommandMap,
Expand Down Expand Up @@ -145,8 +148,11 @@ def _get_default_provider(
command_coverage: Dict[str, List[str]],
route_default: Optional[Dict[str, Optional[str]]],
) -> Optional[str]:
"""Get the default provider for the given route.
Either pick it from the user defaults or from the command coverage."""
"""
Get the default provider for the given route.

Either pick it from the user defaults or from the command coverage.
"""
cmd_cov_given_route = command_coverage.get(route, None)
command_cov_provider = (
cmd_cov_given_route[0] if cmd_cov_given_route else None
Expand Down Expand Up @@ -175,8 +181,7 @@ def validate_kwargs(
func: Callable,
kwargs: Dict[str, Any],
) -> Dict[str, Any]:
"""Validate kwargs and if possible coerce to the correct type"""

"""Validate kwargs and if possible coerce to the correct type."""
config = ConfigDict(extra="allow", arbitrary_types_allowed=True)

sig = signature(func)
Expand All @@ -202,6 +207,7 @@ def build(
route: str,
kwargs: Dict[str, Any],
) -> Dict[str, Any]:
"""Build the parameters for a function."""
func = cls.get_polished_func(func=func)
system_settings = execution_context.system_settings
user_settings = execution_context.user_settings
Expand Down Expand Up @@ -230,9 +236,11 @@ def build(


class StaticCommandRunner:
"""Static Command Runner."""

@classmethod
async def _command(cls, func: Callable, kwargs: Dict[str, Any]) -> OBBject:
"""Run a command and return the output"""
"""Run a command and return the output."""
context_manager: Union[warnings.catch_warnings, ContextManager[None]] = (
warnings.catch_warnings(record=True)
if not Env().DEBUG_MODE
Expand Down Expand Up @@ -281,7 +289,7 @@ async def _execute_func(
func: Callable,
kwargs: Dict[str, Any],
) -> OBBject:
"""Execute a function and return the output"""
"""Execute a function and return the output."""
user_settings = execution_context.user_settings
system_settings = execution_context.system_settings

Expand Down Expand Up @@ -356,6 +364,7 @@ async def run(
*args,
**kwargs,
) -> OBBject:
"""Run a command and return the OBBject as output."""
timestamp = datetime.now()
start_ns = perf_counter_ns()

Expand Down Expand Up @@ -391,33 +400,42 @@ async def run(


class CommandRunner:
"""Command runner."""

def __init__(
self,
command_map: Optional[CommandMap] = None,
system_settings: Optional[SystemSettings] = None,
user_settings: Optional[UserSettings] = None,
) -> None:
"""Initialize the command runner."""
self._command_map = command_map or CommandMap()
self._system_settings = system_settings or SystemService().system_settings
self._user_settings = user_settings or UserService.read_default_user_settings()

self._logging_service = LoggingService(
self._charting_service = ChartingService(
system_settings=self._system_settings, user_settings=self._user_settings
)
self._charting_service = ChartingService(

def init_logging_service(self) -> None:
"""Initialize the logging service."""
_ = LoggingService(
system_settings=self._system_settings, user_settings=self._user_settings
)

@property
def command_map(self) -> CommandMap:
"""Command map."""
return self._command_map

@property
def system_settings(self) -> SystemSettings:
"""System settings."""
return self._system_settings

@property
def user_settings(self) -> UserSettings:
"""User settings."""
return self._user_settings

@user_settings.setter
Expand Down
Original file line number Diff line number Diff line change
@@ -1,57 +1,58 @@
# IMPORT STANDARD
"""Posthog Handler."""
import json
import logging
import re
import warnings
from copy import deepcopy
from enum import Enum
from typing import Any, Dict

import posthog
from openbb_core.app.logs.formatters.formatter_with_exceptions import (
FormatterWithExceptions,
)
from openbb_core.app.logs.models.logging_settings import LoggingSettings
from openbb_core.env import Env
from posthog import Posthog

openbb_posthog = Posthog(
"phc_6FXLqu4uW9yxfyN8DpPdgzCdlYXOmIWdMGh6GnBgJLX", # pragma: allowlist secret
host="https://app.posthog.com",
)


class PosthogHandler(logging.Handler):
"""Posthog Handler"""
"""Posthog Handler."""

class AllowedEvents(Enum):
"""Allowed Posthog Events"""
"""Allowed Posthog Events."""

log_startup = "log_startup"
log_cmd = "log_cmd"
log_error = "log_error"
log_warning = "log_warning"

def __init__(self, settings: LoggingSettings):
"""Initialize Posthog Handler."""
super().__init__()
self._settings = settings
self.logged_in = False
posthog.api_key = "phc_6FXLqu4uW9yxfyN8DpPdgzCdlYXOmIWdMGh6GnBgJLX" # pragma: allowlist secret
posthog.host = "https://app.posthog.com"

@property
def settings(self) -> LoggingSettings:
"""Get logging settings."""
return deepcopy(self._settings)

@settings.setter
def settings(self, settings: LoggingSettings) -> None:
self._settings = settings

def emit(self, record: logging.LogRecord):
"""Emit log record."""
try:
self.send(record=record)
except Exception:
except Exception as e:
self.handleError(record)
if Env().DEBUG_MODE:
raise e

def log_to_dict(self, log_info: str) -> dict:
"""Log to dict"""
"""Log to dict."""
log_regex = r"(STARTUP|CMD|ERROR): (.*)"
log_dict: Dict[str, Any] = {}

Expand All @@ -61,8 +62,7 @@ def log_to_dict(self, log_info: str) -> dict:
return log_dict

def send(self, record: logging.LogRecord):
"""Send log record to Posthog"""

"""Send log record to Posthog."""
level_name = logging.getLevelName(record.levelno)
log_line = FormatterWithExceptions.filter_log_line(text=record.getMessage())

Expand All @@ -85,26 +85,23 @@ def send(self, record: logging.LogRecord):

if not self.logged_in and self._settings.user_id:
self.logged_in = True
openbb_posthog.identify(
posthog.identify(
self._settings.user_id,
{
"email": self._settings.user_email,
"primaryUsage": self._settings.user_primary_usage,
},
)
openbb_posthog.alias(self._settings.user_id, self._settings.app_id)
posthog.alias(self._settings.user_id, self._settings.app_id)

result = openbb_posthog.capture(
posthog.capture(
self._settings.app_id,
event_name,
properties=log_extra,
)
if Env().DEBUG_MODE:
warnings.warn(result)

def extract_log_extra(self, record: logging.LogRecord) -> Dict[str, Any]:
"""Extract log extra from record"""

"""Extract log extra from record."""
log_extra: Dict[str, Any] = {
"appName": self._settings.app_name,
"subAppName": self._settings.sub_app_name,
Expand Down
1 change: 1 addition & 0 deletions openbb_platform/core/openbb_core/app/static/app_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class BaseApp:
"""Base app."""

def __init__(self, command_runner: CommandRunner):
command_runner.init_logging_service()
self._command_runner = command_runner
self._account = Account(self)
self._coverage = Coverage(self)
Expand Down
Loading
Loading