Cat Client for Python
pycat can be used both in python2 (>=2.7) and python3 (>=3.5).
That also means
centos6 is not supported by default due to its built-in python version is
On the otherwise, you can upgrade the built-in
python to higher version or use a
pip install cat-sdk
python setup.py install
docker build -f docker/alpine.df . -t pycat:alpine docker build -f docker/centos7.df . -t pycat:centos7 docker build -f docker/ubuntu1404.df . -t pycat:ubuntu18.04 docker build -f docker/ubuntu1604.df . -t pycat:ubuntu18.04 docker build -f docker/ubuntu1804.df . -t pycat:ubuntu18.04
Some preparations needs to be done before initializing
Then you can initialize
pycat with the following codes:
Only English characters (a-z, A-Z), numbers (0-9), underscore (_) and dash (-) are allowed in appkey.
Since we are using
ThreadLocal to storage the transaction stack in
ccat, which is necessary to build the
message tree, and
pycat is highly dependent on
ccat. (with cffi)
We don't support message tree in
coroutine modes, like
greenlet because different coroutines in the same thread run alternately.
In these cases, you should use the following code to initialize
Then we will disable the context manager which is used for building
Sampling is enabled by default, you can disable it through the following codes.
The default encoder is
binary encoder, you can switch it to
text encoder, which can be recognized by the earlier version of the cat server.
Sometimes you may want to enable the debug log.
Note the logs will be output to
import cat import time cat.init("appkey") with cat.Transaction("foo", "bar") as t: try: t.add_data("a=1") cat.log_event("hook", "before") # do something except Exception as e: cat.log_exception(e) finally: cat.metric("api-count").count() cat.metric("api-duration").duration(100) cat.log_event("hook", "after") time.sleep(1)
t = cat.Transaction("Trans", "t3") t.complete()
To avoid forgetting to complete the transaction, we strongly recommend you to surround the transaction by a
try-finally block and complete the transaction in the
finally code block.
try: t = cat.Transaction("Trans", "t3") finally: t.complete()
We also provide
context manager usages, which can complete the transaction automatically.
And we highly recommend you to use the transaction in these ways.
@cat.transaction("Trans", "T2") def test(): ''' Use with decorator ''' cat.log_event("Event", "E2")
If something goes wrong in the decorated function, the status of the transaction will be set to
FAILED, and whether the function raised an exception or not, the transaction will be auto-completed.
The only problem is that you can't get the transaction object if you monitor a function via a decorator.
via context manager
with cat.Transaction("Transaction", "T1") as t: cat.log_event("Event", "E1") try: do_something() except Exception: t.set_status(cat.CAT_ERROR) t.add_data("hello world!")
If something goes wrong in the
with context, the status of the transaction will be set to
FAILED, and whether the code block raised an exception or not, the transaction will be auto-completed.
Though it is a bit complex, you can get the transaction object :)
We offered a series of APIs to modify the transaction.
These APIs can be easily used with the following codes.
try: trans = cat.Transaction("Trans", "T3") trans.add_data("content") trans.add_data("key", "val") trans.set_status("error") trans.set_duration(500) trans.set_duration_start(time.time() * 1000 - 30 * 1000) trans.set_timestamp(time.time() * 1000 - 30 * 1000) finally: # NOTE don't forget to complete the transaction! trans.complete()
There is something you may want to know:
- You can call
add_dataseveral times, the added data will be connected by
- It's meaningless to specify
durationStartin the same transaction, although we did so in the example :)
- Never forget to complete the transaction! Or you will get corrupted message trees and memory leaks!
# Log a event with success status and empty data. cat.log_event("Event", "E1") # The 3rd parameter (status) is optional, default is "0". # It can be any of string value. # The event will be treated as a "problem" unless the given status == cat.CAT_CUSSESS ("0") # which will be recorded in our problem report. cat.log_event("Event", "E2", cat.CAT_ERROR) cat.log_event("Event", "E3", "failed") # The 4th parameter (data) is optional, default is "". # It can be any of string value. cat.log_event("Event", "E4", "failed", "some debug info")
Log an exception.
Exception is a special event, with
type = Exception and
name = exc.__class__.__name__ by default.
Due to an exception is usually in an except block, the error traces will be automatically collected and reported.
try: raise Exception("I'm a exception") except Exception as e: cat.log_exception(e) # We will collect error traces automatically in most cases # But you can also customize the trace info. try: 1 / 0 except Exception as e: cat.log_exception(e, traceback.format_exc()) # Even out of an except block. e = Exception("something goes wrong") cat.log_exception(e, "customized trace info")
Log an error.
Error is a light exception, with
type = Exception and name given by the 1st parameter.
# Same as cat.log_event("Exception", "e1") cat.log_error("e1") # Error traces will be collected when you use it in an except block. try: 1 / 0 except Exception: cat.log_error("e2") # The 2nd parameter is optional. It is used for customizing your own error traces. cat.log_error("e3", "this is my error stack info")
# Counter cat.metric("metric1").count() # default is 1 cat.metric("metric1").count(5) # Duration cat.metric("metric2").duration(100)
We do aggregation every second.
For example, if you have called count 3 times in one second (with the same name), we will just summarize the value of them and report once to the server.
In the case of
duration, we use
averaged value instead of