Package: phanos
PHANOSPython client to gather data for Prometheus logging in server with multiple instances and workers.
Phanos works with synchronous programs and with asynchronous programs implemented by asyncio.
Behavior in multithread programs is unspecified.ProfilingDefault metricsPhanos contains two default metrics. Time profiler measuring execution time of
decorated methods is ON by default. Response size profiler measuring size of methods return value
of root method is OFF by default. Both can be deleted byphanos.profiler.delete_metric(phanos.publisher.TIME_PROFILER)andphanos.profiler.delete_metric(phanos.publisher.RESPONSE_SIZE).ConfigurationThere are options of how to configure profiler. Profiler must be configured by one of these options.Both options uses these attributes:jobjoblabelfor prometheus; usually name of applogger(optional)name of logger; if not specified, Phanos creates its own loggertime_profile(optional)by defaultprofilertracks execution time of profiled function/objectresponse_size_profile(optional)by defaultprofilerwill not track size of return valuehandle_records(optional)by defaultprofilermeasures values and handles them;
if False then no profiling is made or handlederror_raised_label(optional)by default profiler will not add labelerror_raisedto each
record; if set, every record will have information if given profiled function/method raised error; if error raised,
profiling will be logged even if noLoggerHandlerexistshandlers(optional)serialized named handlers to publish profiled records;
if no handlers specified then no measurements are made; for handlers description refer toHandlers.classclass handler to initializedhandler_namehandler name required argument of publishers**other arguments- specific arguments required to construct instance of class f.e.:output.Dict ConfigurationIt is possible to configure profile with configration dictionary with methodProfiler.dict_config(settings)(similar tologgingdictConfig).Example of configuration dict:settings={"job":"my_app","logger":"my_app_debug_logger","time_profile":True,"request_size_profile":False,"handle_records":True,"error_raised_label":False,"handlers":{"stdout_handler_ref":{"class":"phanos.handlers.StreamHandler","handler_name":"stdout_handler","output":"ext://sys.stdout"}}}Configuration in codeWhen configuring in code useProfiler.configmethod  to configure profiling.
For handler addition create handler instance first and add it to profiler withProfiler.add_handlermethod.Example of configuration:importphanos# some codeclassSomeApp(Flask):"""some code"""phanos.profiler.config(logger,time_profile,resp_size_profile,handle_records,error_raised_label)log_handler=phanos.handlers.LoggerHandler('handler_name',logger_instance,logging_level)phanos.profiler.add_handler(log_handler)# some codeUsageconfigure profiler as shown inConfigurationdecorate methods which you want to profile@phanos.profileshortcut for@phanos.profiler.profile.
Allways put decorator closest to method definition as possible, because the decorator collides with some other
decorators. The decorator must allways be under@classmethod,@staticmethod,flask_restxdecorators and possibly others.importphanosclassSomeClass:""" Example of @profile decorator placement"""@staticmethod@phanos.profiledefsome_method():pass# is equivalent to@staticmethod@phanos.profiler.profiledefsome_method():passHandlersEach handler havehandler_nameargument. This string can be used to delete handlers later
withphanos.profiler.delete_handler(handler_name).Records can be handled by these handlers:StreamHandler(handler_name, output)- write records to given output (default issys.stdout)LoggerHandler(handler_name, logger, level)- logs string representation of records with given logger and with given
level; default level islogging.DEBUG; if no logger is passed, Phanos creates its own loggerNamedLoggerHandler(handler_name, logger_name, level)- same as LoggerHandler, butloggerinstance is found bylogging.getLogger(logger_name)method.ImpProfHandler(handler_name, **rabbit_connection_params, logger)- sending records to RabbitMQ queue.Phanos metrics:Basic Prometheus metrics:HistogramSummaryCounterInfoGaugeEnumThese classes represent basic Prometheus metrics types. For more information about Prometheus metric types, mainly
allowed operations, refer toPrometheus documentation.Custom metricstime profiler: metric for measuring time-consuming actions in mS; basically Histogram metric of Prometheus.response size profiler: metric for measuring return value of method in bytes, designed to measure response
size of endpoints; basically Histogram metric of Prometheus.Creating new custom metricnew metric class needs to inherit from one of basic Prometheus metrics.__init__()method needs to callsuper().__init__()implement method for each operation wanted; this method must call one of inherited metrics operations if you want
operation to be saved f.e.Gauge.dec;MetricWrapper.cleanup()is called after all measured metrics are handled; if custom cleanup is needed,
implement methodcleanup()callingsuper().cleanup()insideAdd metrics automatic measurementsphanos.profilercontains these four arguments:before_func : callable - executes before each profiled method/functionafter_func : callable - executes after each profiled method/functionbefore_root_func : callable - executes before each profiled root method/function (first method in profiling tree)after_root_func : callable - executes after each profiled root method/function (first method in profiling tree)Implement these methods with all needed measurement.Complete exampleimporttypingimportphanos.treeimportphanos# custom metric exampleclassCustomMetric(phanos.metrics.Counter):def__init__(self,name,job,units,labels):super().__init__(name,job,units,labels)# custom initializationself.count=0defcustom_op(self,value:int=0,label_values:typing.Optional[typing.Dict[str,str]]=None):self.count+=valueself.inc(self.count,label_values)defhelper_method(self):passdefcleanup(self)->None:super().cleanup()self.count=0my_metric=CustomMetric(name="name",job="MyJob",units="units",labels=["label_name"])defbefore_function(func,args,kwargs):# this operation will be recordedmy_metric.custom_op(2,{"label_name":"label_value"})# this won't be recordedmy_metric.helper_method()phanos.profiler.before_func=before_functionWhat must/can be done:custom metricMetricWrapper.__init__needsname,job,unitsarguments passed;labelsandloggerare optionalcustom measurementsbefore_*functions must havefuncargument, where function which is executed is passed.after_*function needs to havefn_resultargument where function result is passedall four functions can accessargsandkwargsof decorated methods. These arguments are passed
in packed form.