7
7
import logging
8
8
from collections import Counter
9
9
from dataclasses import dataclass
10
+ from typing import Optional
10
11
11
12
from pants .engine .internals .scheduler import Workunit
12
13
from pants .engine .rules import collect_rules , rule
17
18
WorkunitsCallbackFactoryRequest ,
18
19
)
19
20
from pants .engine .unions import UnionRule
20
- from pants .option .option_types import BoolOption
21
+ from pants .option .option_types import BoolOption , StrOption
21
22
from pants .option .subsystem import Subsystem
22
23
from pants .util .collections import deep_getsizeof
23
24
from pants .util .strutil import softwrap
@@ -55,13 +56,21 @@ class StatsAggregatorSubsystem(Subsystem):
55
56
),
56
57
advanced = True ,
57
58
)
59
+ output_file = StrOption (
60
+ default = None ,
61
+ metavar = "<path>" ,
62
+ help = "Output the stats to this file. If unspecified, outputs to stdout." ,
63
+ )
58
64
59
65
60
66
class StatsAggregatorCallback (WorkunitsCallback ):
61
- def __init__ (self , * , log : bool , memory : bool , has_histogram_module : bool ) -> None :
67
+ def __init__ (
68
+ self , * , log : bool , memory : bool , output_file : Optional [str ], has_histogram_module : bool
69
+ ) -> None :
62
70
super ().__init__ ()
63
71
self .log = log
64
72
self .memory = memory
73
+ self .output_file = output_file
65
74
self .has_histogram_module = has_histogram_module
66
75
67
76
@property
@@ -80,6 +89,8 @@ def __call__(
80
89
if not finished :
81
90
return
82
91
92
+ output_contents = ""
93
+
83
94
if self .log :
84
95
# Capture global counters.
85
96
counters = Counter (context .get_metrics ())
@@ -93,7 +104,7 @@ def __call__(
93
104
counter_lines = "\n " .join (
94
105
f" { name } : { count } " for name , count in sorted (counters .items ())
95
106
)
96
- logger . info ( f"Counters:\n { counter_lines } " )
107
+ output_contents += f"Counters:\n { counter_lines } "
97
108
98
109
if self .memory :
99
110
ids : set [int ] = set ()
@@ -115,18 +126,20 @@ def __call__(
115
126
memory_lines = "\n " .join (
116
127
f" { size } \t \t { count } \t \t { name } " for size , count , name in sorted (entries )
117
128
)
118
- logger .info (f"Memory summary (total size in bytes, count, name):\n { memory_lines } " )
129
+ output_contents += (
130
+ f"\n Memory summary (total size in bytes, count, name):\n { memory_lines } "
131
+ )
119
132
120
133
if not (self .log and self .has_histogram_module ):
121
134
return
122
135
from hdrh .histogram import HdrHistogram # pants: no-infer-dep
123
136
124
137
histograms = context .get_observation_histograms ()["histograms" ]
125
138
if not histograms :
126
- logger . info ( "No observation histogram were recorded.")
139
+ output_contents += " \n No observation histogram were recorded."
127
140
return
128
141
129
- logger . info ( "Observation histogram summaries:")
142
+ output_contents += " \n Observation histogram summaries:"
130
143
for name , encoded_histogram in histograms .items ():
131
144
# Note: The Python library for HDR Histogram will only decode compressed histograms
132
145
# that are further encoded with base64. See
@@ -138,8 +151,8 @@ def __call__(
138
151
[25 , 50 , 75 , 90 , 95 , 99 ]
139
152
).items ()
140
153
)
141
- logger . info (
142
- f"Summary of `{ name } ` observation histogram:\n "
154
+ output_contents += (
155
+ f"\n Summary of `{ name } ` observation histogram:\n "
143
156
f" min: { histogram .get_min_value ()} \n "
144
157
f" max: { histogram .get_max_value ()} \n "
145
158
f" mean: { histogram .get_mean_value ():.3f} \n "
@@ -149,6 +162,12 @@ def __call__(
149
162
f"{ percentile_to_vals } "
150
163
)
151
164
165
+ if self .output_file :
166
+ with open (self .output_file , "w" ) as fh :
167
+ fh .write (output_contents )
168
+ else :
169
+ logger .info (output_contents )
170
+
152
171
153
172
@dataclass (frozen = True )
154
173
class StatsAggregatorCallbackFactoryRequest :
@@ -178,6 +197,7 @@ def construct_callback(
178
197
StatsAggregatorCallback (
179
198
log = subsystem .log ,
180
199
memory = subsystem .memory_summary ,
200
+ output_file = subsystem .output_file ,
181
201
has_histogram_module = has_histogram_module ,
182
202
)
183
203
if subsystem .log or subsystem .memory_summary
0 commit comments