Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/opencensus/trace/stack_trace.py: 30%
64 statements
« prev ^ index » next coverage.py v7.3.2, created at 2023-12-06 06:04 +0000
« prev ^ index » next coverage.py v7.3.2, created at 2023-12-06 06:04 +0000
1# Copyright 2017, OpenCensus Authors
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
15import hashlib
16import os
17import random
18import traceback
20from opencensus.common.utils import get_truncatable_str
22MAX_FRAMES = 128
24BUILD_ID = os.environ.get('BUILD_ID', 'unknown')
25SOURCE_VERSION = os.environ.get('SOURCE_VERSION', 'unknown')
28class StackFrame(object):
29 """Represents a single stack frame in a stack trace.
31 :type func_name: str
32 :param func_name: The fully-qualified name that uniquely identifies the
33 function or method that is active in this frame (up to
34 1024 bytes).
36 :type original_func_name: str
37 :param original_func_name: An un-mangled function name, if functionName is
38 mangled. The name can be fully-qualified
39 (up to 1024 bytes).
41 :type file_name: str
42 :param file_name: The name of the source file where the function call
43 appears (up to 256 bytes).
45 :type line_num: int
46 :param line_num: The line number in fileName where the function call
47 appears.
49 :type col_num: int
50 :param col_num: The column number where the function call appears, if
51 available. This is important in JavaScript because of its
52 anonymous functions.
54 :type load_module: str
55 :param load_module: For example: main binary, kernel modules, and dynamic
56 libraries such as libc.so, sharedlib.so
57 (up to 256 bytes).
59 :type build_id: str
60 :param build_id: A unique identifier for the module, usually a hash of its
61 contents (up to 128 bytes).
64 :type source_version: str
65 :param source_version: The version of the deployed source code
66 (up to 128 bytes).
67 """
68 def __init__(self,
69 func_name,
70 original_func_name,
71 file_name,
72 line_num,
73 col_num,
74 load_module,
75 build_id,
76 source_version):
77 self.func_name = func_name
78 self.original_func_name = original_func_name
79 self.file_name = file_name
80 self.line_num = line_num
81 self.col_num = col_num
82 self.load_module = load_module
83 self.build_id = build_id
84 self.source_version = source_version
86 def format_stack_frame_json(self):
87 """Convert StackFrame object to json format."""
88 stack_frame_json = {}
89 stack_frame_json['function_name'] = get_truncatable_str(
90 self.func_name)
91 stack_frame_json['original_function_name'] = get_truncatable_str(
92 self.original_func_name)
93 stack_frame_json['file_name'] = get_truncatable_str(self.file_name)
94 stack_frame_json['line_number'] = self.line_num
95 stack_frame_json['column_number'] = self.col_num
96 stack_frame_json['load_module'] = {
97 'module': get_truncatable_str(self.load_module),
98 'build_id': get_truncatable_str(self.build_id),
99 }
100 stack_frame_json['source_version'] = get_truncatable_str(
101 self.source_version)
103 return stack_frame_json
106class StackTrace(object):
107 """A call stack appearing in a trace.
109 :type stack_frames: list
110 :param stack_frames: Stack frames in this stack trace. A maximum of 128
111 frames are allowed.
113 :type stack_trace_hash_id: str
114 :param stack_trace_hash_id: The hash ID is used to conserve network
115 bandwidth for duplicate stack traces within a
116 single trace.
117 """
118 def __init__(self, stack_frames=None, stack_trace_hash_id=None):
119 if stack_frames is None:
120 stack_frames = []
121 if len(stack_frames) > MAX_FRAMES:
122 self.dropped_frames_count = len(stack_frames) - MAX_FRAMES
123 stack_frames = stack_frames[-MAX_FRAMES:]
124 else:
125 self.dropped_frames_count = 0
127 if stack_trace_hash_id is None:
128 stack_trace_hash_id = generate_hash_id()
130 self.stack_frames = stack_frames
131 self.stack_trace_hash_id = stack_trace_hash_id
133 @classmethod
134 def from_traceback(cls, tb):
135 """Initializes a StackTrace from a python traceback instance"""
136 stack_trace = cls(
137 stack_trace_hash_id=generate_hash_id_from_traceback(tb)
138 )
139 # use the add_stack_frame so that json formatting is applied
140 for tb_frame_info in traceback.extract_tb(tb):
141 filename, line_num, fn_name, _ = tb_frame_info
142 stack_trace.add_stack_frame(
143 StackFrame(
144 func_name=fn_name,
145 original_func_name=fn_name,
146 file_name=filename,
147 line_num=line_num,
148 col_num=0, # I don't think this is available in python
149 load_module=filename,
150 build_id=BUILD_ID,
151 source_version=SOURCE_VERSION
152 )
153 )
154 return stack_trace
156 def add_stack_frame(self, stack_frame):
157 """Add StackFrame to frames list."""
158 if len(self.stack_frames) >= MAX_FRAMES:
159 self.dropped_frames_count += 1
160 else:
161 self.stack_frames.append(stack_frame.format_stack_frame_json())
163 def format_stack_trace_json(self):
164 """Convert a StackTrace object to json format."""
165 stack_trace_json = {}
167 if self.stack_frames:
168 stack_trace_json['stack_frames'] = {
169 'frame': self.stack_frames,
170 'dropped_frames_count': self.dropped_frames_count
171 }
173 stack_trace_json['stack_trace_hash_id'] = self.stack_trace_hash_id
175 return stack_trace_json
178def generate_hash_id():
179 """Generate a hash id."""
180 return random.getrandbits(64)
183def generate_hash_id_from_traceback(tb):
184 m = hashlib.md5() # nosec
185 for tb_line in traceback.format_tb(tb):
186 m.update(tb_line.encode('utf-8'))
187 # truncate the hash for easier compatibility with StackDriver,
188 # should still be unique enough to avoid collisions
189 return int(m.hexdigest()[:12], 16)