-
Notifications
You must be signed in to change notification settings - Fork 333
Expand file tree
/
Copy pathTracerFlareManager.java
More file actions
200 lines (176 loc) · 6.97 KB
/
TracerFlareManager.java
File metadata and controls
200 lines (176 loc) · 6.97 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
package datadog.flare;
import datadog.trace.api.Config;
import datadog.trace.api.flare.TracerFlare;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
import javax.management.InstanceAlreadyExistsException;
import javax.management.MBeanRegistrationException;
import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.NotCompliantMBeanException;
import javax.management.ObjectName;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* MBean implementation for managing and accessing tracer flare data.
*
* <p>This class provides JMX operations to list flare data sources and retrieve flare data either
* for individual sources or a complete flare archive. See {@link TracerFlareManagerMBean} for
* documentation on the exposed operations.
*/
public class TracerFlareManager implements TracerFlareManagerMBean {
private static final Logger LOGGER = LoggerFactory.getLogger(TracerFlareManager.class);
private final TracerFlareService flareService;
protected ObjectName mbeanName;
public TracerFlareManager(TracerFlareService flareService) {
this.flareService = flareService;
}
@Override
public String generateFullFlareZip() throws IOException {
TracerFlare.prepareForFlare();
long currentMillis = System.currentTimeMillis();
boolean dumpThreads = Config.get().isTriageEnabled() || LOGGER.isDebugEnabled();
byte[] zipBytes = flareService.buildFlareZip(currentMillis, currentMillis, dumpThreads);
return Base64.getEncoder().encodeToString(zipBytes);
}
@Override
public String listFlareFiles() throws IOException {
TracerFlare.prepareForFlare();
StringBuilder result = new StringBuilder();
for (Map.Entry<String, String[]> entry : TracerFlareService.BUILT_IN_SOURCES.entrySet()) {
String sourceName = entry.getKey();
String[] files = entry.getValue();
for (String filename : files) {
result.append(sourceName).append(" ").append(filename).append("\n");
}
}
for (TracerFlare.Reporter reporter : TracerFlare.getReporters()) {
try (ByteArrayOutputStream bytes = new ByteArrayOutputStream();
ZipOutputStream zip = new ZipOutputStream(bytes)) {
reporter.addReportToFlare(zip);
zip.finish();
try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes.toByteArray());
ZipInputStream zis = new ZipInputStream(bais)) {
ZipEntry entry;
while ((entry = zis.getNextEntry()) != null) {
result
.append(reporter.getClass().getName())
.append(" ")
.append(entry.getName())
.append("\n");
zis.closeEntry();
}
}
} catch (IOException e) {
LOGGER.debug("Failed to inspect reporter {}", reporter.getClass().getName(), e);
}
}
return result.toString();
}
@Override
public String getFlareFile(String sourceName, String filename) throws IOException {
final byte[] zipBytes;
if (isBuiltInSource(sourceName)) {
zipBytes = flareService.getBuiltInSourceZip(sourceName);
} else {
zipBytes = getReporterFile(sourceName);
}
return extractFileFromZip(zipBytes, filename);
}
private boolean isBuiltInSource(String sourceName) {
return TracerFlareService.BUILT_IN_SOURCES.containsKey(sourceName);
}
/**
* Generates flare data for a specific reporter.
*
* <p>The reporter's data is generated as a ZIP file, and the specified filename is extracted. If
* the file is text, it is returned as plain text; if binary, it is returned base64-encoded.
*
* @param reporterClassName the fully qualified class name of the reporter
* @return the zip file containing the reporter's content
* @throws IOException if an error occurs while generating the flare
*/
private byte[] getReporterFile(String reporterClassName) throws IOException {
TracerFlare.Reporter reporter = TracerFlare.getReporter(reporterClassName);
if (reporter == null) {
throw new IOException("Error: Reporter not found: " + reporterClassName);
}
reporter.prepareForFlare();
try (ByteArrayOutputStream bytes = new ByteArrayOutputStream();
ZipOutputStream zip = new ZipOutputStream(bytes)) {
reporter.addReportToFlare(zip);
zip.finish();
return bytes.toByteArray();
}
}
/**
* Extracts a specific file from a ZIP archive.
*
* <p>Searches through the ZIP entries for the specified filename and returns its content. If the
* file name ends in ".txt", it is returned as plain text; if binary, it is returned
* base64-encoded.
*
* @param zipBytes the ZIP file bytes
* @param filename the name of the file to extract
* @return the file content (plain text or base64-encoded binary)
* @throws IOException if an error occurs while reading the ZIP
*/
private String extractFileFromZip(byte[] zipBytes, String filename) throws IOException {
try (ByteArrayInputStream bais = new ByteArrayInputStream(zipBytes);
ZipInputStream zis = new ZipInputStream(bais)) {
ZipEntry entry;
while ((entry = zis.getNextEntry()) != null) {
if (entry.getName().equals(filename)) {
ByteArrayOutputStream content = new ByteArrayOutputStream();
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = zis.read(buffer)) != -1) {
content.write(buffer, 0, bytesRead);
}
zis.closeEntry();
byte[] contentBytes = content.toByteArray();
if (entry.getName().endsWith(".txt")) {
return new String(contentBytes, StandardCharsets.UTF_8);
} else {
return Base64.getEncoder().encodeToString(contentBytes);
}
}
zis.closeEntry();
}
throw new IOException("Failed to extract file: " + filename);
}
}
void registerMBean() {
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
try {
mbeanName = new ObjectName("datadog.flare:type=TracerFlare");
mbs.registerMBean(this, mbeanName);
LOGGER.info("Registered TracerFlare MBean at {}", mbeanName);
} catch (MalformedObjectNameException
| InstanceAlreadyExistsException
| MBeanRegistrationException
| NotCompliantMBeanException e) {
LOGGER.warn("Failed to register TracerFlare MBean", e);
mbeanName = null;
}
}
void unregisterMBean() {
if (mbeanName != null) {
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
try {
mbs.unregisterMBean(mbeanName);
LOGGER.debug("Unregistered TracerFlare MBean");
} catch (Exception e) {
LOGGER.warn("Failed to unregister TracerFlare MBean", e);
}
}
}
}