The 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 2.6.6
.
On the otherwise, you can upgrade the built-in python
to higher version or use a virtualenv
instead.
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:ubuntu14.04
docker build -f docker/ubuntu1604.df . -t pycat:ubuntu16.04
docker build -f docker/ubuntu1804.df . -t pycat:ubuntu18.04
Some preparations needs to be done before initializing pycat
.
Then you can initialize pycat
with the following codes:
cat.init("appkey")
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 gevent
, greenlet
because different coroutines in the same thread run alternately.
In these cases, you should use the following code to initialize pycat
.
cat.init("appkey", logview=False)
Then we will disable the context manager which is used for building message tree
.
Sampling is enabled by default, you can disable it through the following codes.
cat.init("appkey", sampling=False)
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.
cat.init("appkey", encoder=cat.ENCODER_TEXT)
Sometimes you may want to enable the debug log.
Note the logs will be output to console
.
cat.init("appkey", debug=True)
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 decorator
and 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.
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.
- add_data
- set_status
- set_duration
- set_duration_start
- set_timestamp
- complete
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_data
several times, the added data will be connected by&
. - It's meaningless to specify
duration
anddurationStart
in 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 summarized
value.