Skip to content

Commit 0ca9652

Browse files
committed
Add logger helper functions from detectron2 (licensed as Apache 2.0)
1 parent 7dbec31 commit 0ca9652

File tree

1 file changed

+121
-0
lines changed

1 file changed

+121
-0
lines changed
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
#
2+
# Licensed to the Apache Software Foundation (ASF) under one or more
3+
# contributor license agreements. See the NOTICE file distributed with
4+
# this work for additional information regarding copyright ownership.
5+
# The ASF licenses this file to You under the Apache License, Version 2.0
6+
# (the "License"); you may not use this file except in compliance with
7+
# the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
#
17+
18+
"""Helper functions for easier logging.
19+
20+
This module provides a few convenient logging methods, some of which
21+
were adopted from
22+
https://github.com/abseil/abseil-py/blob/master/absl/logging/__init__.py
23+
in
24+
https://github.com/facebookresearch/detectron2/blob/main/detectron2/utils/logger.py
25+
"""
26+
27+
import sys
28+
29+
30+
def _find_caller():
31+
"""
32+
Returns:
33+
str: module name of the caller
34+
tuple: a hashable key to be used to identify different callers
35+
"""
36+
frame = sys._getframe(2)
37+
while frame:
38+
code = frame.f_code
39+
if os.path.join("utils", "logger.") not in code.co_filename:
40+
mod_name = frame.f_globals["__name__"]
41+
if mod_name == "__main__":
42+
mod_name = "detectron2"
43+
return mod_name, (code.co_filename, frame.f_lineno, code.co_name)
44+
frame = frame.f_back
45+
46+
47+
_LOG_COUNTER = Counter()
48+
_LOG_TIMER = {}
49+
50+
51+
def log_first_n(lvl, msg, n=1, *, name=None, key="caller"):
52+
"""
53+
Log only for the first n times.
54+
55+
Args:
56+
lvl (int): the logging level
57+
msg (str):
58+
n (int):
59+
name (str): name of the logger to use. Will use the caller's module
60+
by default.
61+
key (str or tuple[str]): the string(s) can be one of "caller" or
62+
"message", which defines how to identify duplicated logs.
63+
For example, if called with `n=1, key="caller"`, this function
64+
will only log the first call from the same caller, regardless of
65+
the message content.
66+
If called with `n=1, key="message"`, this function will log the
67+
same content only once, even if they are called from different
68+
places. If called with `n=1, key=("caller", "message")`, this
69+
function will not log only if the same caller has logged the same
70+
message before.
71+
"""
72+
if isinstance(key, str):
73+
key = (key, )
74+
assert len(key) > 0
75+
76+
caller_module, caller_key = _find_caller()
77+
hash_key = ()
78+
if "caller" in key:
79+
hash_key = hash_key + caller_key
80+
if "message" in key:
81+
hash_key = hash_key + (msg, )
82+
83+
_LOG_COUNTER[hash_key] += 1
84+
if _LOG_COUNTER[hash_key] <= n:
85+
logging.getLogger(name or caller_module).log(lvl, msg)
86+
87+
88+
def log_every_n(lvl, msg, n=1, *, name=None):
89+
"""
90+
Log once per n times.
91+
92+
Args:
93+
lvl (int): the logging level
94+
msg (str):
95+
n (int):
96+
name (str): name of the logger to use. Will use the caller's module
97+
by default.
98+
"""
99+
caller_module, key = _find_caller()
100+
_LOG_COUNTER[key] += 1
101+
if n == 1 or _LOG_COUNTER[key] % n == 1:
102+
logging.getLogger(name or caller_module).log(lvl, msg)
103+
104+
105+
def log_every_n_seconds(lvl, msg, n=1, *, name=None):
106+
"""
107+
Log no more than once per n seconds.
108+
109+
Args:
110+
lvl (int): the logging level
111+
msg (str):
112+
n (int):
113+
name (str): name of the logger to use. Will use the caller's module
114+
by default.
115+
"""
116+
caller_module, key = _find_caller()
117+
last_logged = _LOG_TIMER.get(key, None)
118+
current_time = time.time()
119+
if last_logged is None or current_time - last_logged >= n:
120+
logging.getLogger(name or caller_module).log(lvl, msg)
121+
_LOG_TIMER[key] = current_time

0 commit comments

Comments
 (0)