def test_disabled(config): config['SENTRY']['DSN'] = None container = Mock(config=config) reporter = SentryReporter().bind(container, "sentry") reporter.setup() # DSN applied correctly assert reporter.client.get_public_dsn() is None assert not reporter.client.is_enabled()
def test_setup_without_optional_config(config): del config['SENTRY']['CLIENT_CONFIG'] container = Mock(config=config) reporter = SentryReporter().bind(container, "sentry") reporter.setup() # DSN applied correctly assert reporter.client.get_public_dsn() == "//user@localhost:9000/1" assert reporter.client.is_enabled() # transport set correctly transport = reporter.client.remote.get_transport() assert isinstance(transport, EventletHTTPTransport)
class Service(object): name = "service" sentry = SentryReporter() @rpc def record_with_helper(self, data): breadcrumbs.record( category="worker", message='breadcrumb message', level='warning', data=data ) raise CustomException("Error!") @rpc def record_directly(self, data): self.sentry.captureBreadcrumb( category="worker", message='breadcrumb message', level='warning', data=data ) raise CustomException("Error!") @rpc def activate_deactivate(self, a, b, c): breadcrumbs.record(category="worker", message=a) self.sentry.context.deactivate() breadcrumbs.record(category="worker", message=b) self.sentry.context.activate() breadcrumbs.record(category="worker", message=c) raise CustomException("Error!")
class Service(object): name = "service" sentry = SentryReporter() @http('GET', '/resource') def resource(self, request): raise CustomException()
class Service(object): name = "service" sentry = SentryReporter() @rpc(expected_exceptions=CustomException) def broken(self): raise exception_cls("Error!")
def test_expected_exception_not_reported( exception_cls, expected_count, config, worker_ctx ): exc = exception_cls("Error!") exc_info = (exception_cls, exc, None) config['SENTRY']['REPORT_EXPECTED_EXCEPTIONS'] = False container = Mock(config=config) reporter = SentryReporter().bind(container, "sentry") reporter.setup() with patch.object(reporter.client, 'captureException') as capture: reporter.worker_result(worker_ctx, None, exc_info) assert capture.call_count == expected_count
class Service(object): name = "service" sentry = SentryReporter() @http('GET', '/resource') def resource(self, request): breadcrumbs.record(message=request.query_string) raise CustomException()
class Service(object): name = "service" sentry = SentryReporter() unsafe = Unsafe() @rpc def broken(self): log.info("breadcrumb %s", self.unsafe) raise CustomException("Error!")
class ErrorTracking: """ Gives your service Sentry.io error tracking. Requires the following values in config.yaml SENTRY: DSN: ${SENTRY_DSN} """ sentry = SentryReporter()
class Service(object): name = "service" sentry = SentryReporter() @rpc(expected_exceptions=CustomException) def broken(self): raise CustomException("Error!") @rpc def fine(self): return "OK"
class HeartbeatService: name = "heartbeat_service" sentry = SentryReporter() dispatch = EventDispatcher() @timer(5) def send_heartbeat(self): timestamp = datetime.now() print("Dispatching a heartbeat with timestamp {}".format(timestamp), flush=True) self.dispatch("heartbeat", timestamp)
class Service(object): name = "service" sentry = SentryReporter() @rpc def broken(self, data): self.sentry.context.merge({"arbitrary": data}) raise CustomException("Error!") @rpc def get_dsn(self): return self.sentry.get_public_dsn()
class GreetingService: name = "greetings_service" sentry = SentryReporter() uid = uuid.uuid4() @rpc @timeout(3) def hello(self, name): time.sleep(random.randint(0, int(os.getenv("RANDOM_TIMEOUT", "0")))) return "Hello {}, {} is calling!".format(name, GreetingService.uid) @event_handler("heartbeat_service", "heartbeat", handler_type=BROADCAST, reliable_delivery=False) def on_heartbeat(self, ts): print("Received Heartbeat with timestamp {}".format(ts), flush=True)
class SampleService: name = "{{ cookiecutter.project_slug }}_service" dispatch = EventDispatcher() sentry = SentryReporter() models = DjangoModels() sampleservice = RpcProxy("{{ cookiecutter.project_slug }}_services") @event_handler( "{{ cookiecutter.project_slug }}", "sync.{{ cookiecutter.project_slug }}.v1", handler_type=SINGLETON, ) def {{ cookiecutter.project_slug }}_sync_handler(self, payload): """ Simple {{ cookiecutter.project_slug }} sample handler """ logging.info("Received event handler %s", {len(payload or [])})
class HttpDemoService: name = "http_demo_service" sentry = SentryReporter() @http("GET", "/broken") def broken(self, request): raise ConnectionRefusedError() @http('GET', '/books/<string:uuid>') def demo_get(self, request, uuid): data = {'id': uuid, 'title': 'The unbearable lightness of being', 'author': 'Milan Kundera'} return Response(json.dumps({'book': data}), mimetype='application/json') @http('POST', '/books') def demo_post(self, request): return Response(json.dumps({'book': request.data.decode()}), mimetype='application/json')
class LoginService: name = "auth_service" secret = settings["JWT_SECRET"] db = Database(Base) sentry = SentryReporter() @staticmethod def create_token(): arr = bytearray(32) for i in range(0, 32): arr[i] = random.randint(0, 255) return base64.encodebytes(arr).decode().rstrip() @rpc(expected_exceptions=InvalidCredentials) @timeout(3) def login(self, username, password): try: rec = self.db.get_session().query(User).filter( User.username == username).one() if password == rec.password: payload = {"userId": rec.userid, "token": self.create_token()} return { "access_token": jwt.encode(payload, self.secret, algorithm="HS256").decode().rstrip() } else: raise InvalidCredentials("Wrong password") except NoResultFound: raise InvalidCredentials("User not found") @event_handler("heartbeat_service", "heartbeat", handler_type=BROADCAST, reliable_delivery=False) def on_heartbeat(self, ts): print("Received Heartbeat with timestamp {}".format(ts), flush=True)
class Service: """ Base class for nameko services. """ name = "no name" SentryLoggerConfig() sentry = SentryReporter() config = Config() container = ContainerProvider() metrics = PrometheusMetrics() @rpc def say_hello(self) -> str: """ RPC method to ping the service to check if it can be reached. """ return f"Hello from {self.name}!" @rpc def query_state(self) -> Dict[str, Any]: """ Returns a detailed state of running service. """ return asdict(ServiceState.from_container(self.container)) @http("GET", "/metrics") def serve_metrics(self, request: Request) -> Response: """ Exposes Prometheus metrics over HTTP. """ return self.metrics.expose_metrics(request) @rpc def set_log_level(self, logger_name: str, level: int) -> str: """ Temporarily override log level in a running service. Useful for example for debugging a live service instance, where your default log level is INFO or higher to avoid clutter in logs. This RPC allows you to change log level while the application is running. For example:: >>> n.rpc.my_service.set_log_level("some.module", logging.DEBUG) Now your logs will include debug messages from ``some.module`` even if your static log configuration (dictConfig etc.) silenced them. Caveat #1: Updating log level in this manner will only affect loggers acquired *after* this RPC call. So your code must call ``logging.get_logger()`` as late as possible. This unfortunately means that library code may or may not be affected - depends on how the library acquires its loggers. Caveat #2: If your service runs in multiple replicas behind a load balancer, you must call this RPC method at least as many times as there are replicas to ensure that each replica will have its log level changed. """ logger = logging.getLogger(__name__) logger_to_change = logging.getLogger(logger_name) logger.info( f"Updating level for {logger_name} from {logger_to_change.level} to {level}" ) message = f""" Log level changed on host {socket.gethostname()}. Revert with: n.rpc.{self.name}.set_log_level({logger_name!r}, {logger_to_change.level!r}) """ logger_to_change.setLevel(level) return message