Skip to content

Commit 34c186f

Browse files
gregwgarrettjonesgoogle
authored andcommitted
Initialize the default MonitoredResource from a GAE environment (#1535)
1 parent 0010dd8 commit 34c186f

3 files changed

Lines changed: 199 additions & 6 deletions

File tree

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/*
2+
* Copyright 2016 Google Inc. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.cloud.logging;
18+
19+
import java.util.logging.LogRecord;
20+
21+
import com.google.cloud.MonitoredResource.Builder;
22+
23+
/**
24+
* A Logging {@link Enhancer} that enhances the logging for the
25+
* GAE Flex environment. This enhancer can
26+
* be configured in a logging.properties file with:
27+
*
28+
* <pre>
29+
* handlers=com.google.cloud.logging.LoggingHandler
30+
* com.google.cloud.logging.LoggingHandler.log=gaeflex.log
31+
* com.google.cloud.logging.LoggingHandler.resourceType=gae_app
32+
* com.google.cloud.logging.LoggingHandler.enhancers=com.google.cloud.logging.GaeFlexLoggingEnhancer
33+
* com.google.cloud.logging.LoggingHandler.formatter = java.util.logging.SimpleFormatter
34+
* java.util.logging.SimpleFormatter.format=%3$s: %5$s%6$s
35+
* </pre>
36+
*
37+
*/
38+
public class GaeFlexLoggingEnhancer implements LoggingHandler.Enhancer {
39+
40+
private static final ThreadLocal<String> traceId = new ThreadLocal<>();
41+
42+
private final String gaeInstanceId;
43+
44+
/**
45+
* Set the Trace ID associated with any logging done by the current thread.
46+
*
47+
* @param id The traceID
48+
*/
49+
public static void setCurrentTraceId(String id) {
50+
traceId.set(id);
51+
}
52+
53+
/**
54+
* Get the Trace ID associated with any logging done by the current thread.
55+
*
56+
* @return id The traceID
57+
*/
58+
public static String getCurrentTraceId() {
59+
return traceId.get();
60+
}
61+
62+
public GaeFlexLoggingEnhancer() {
63+
gaeInstanceId = System.getenv("GAE_INSTANCE"); // Are we running on a GAE instance?
64+
}
65+
66+
@Override
67+
public void enhanceMonitoredResource(Builder builder) {
68+
if (gaeInstanceId != null) {
69+
if (System.getenv("GAE_SERVICE") != null) {
70+
builder.addLabel("module_id", System.getenv("GAE_SERVICE"));
71+
}
72+
if (System.getenv("GAE_VERSION") != null) {
73+
builder.addLabel("version_id", System.getenv("GAE_VERSION"));
74+
}
75+
}
76+
}
77+
78+
@Override
79+
public void enhanceLogEntry(com.google.cloud.logging.LogEntry.Builder builder, LogRecord record) {
80+
if (gaeInstanceId != null) {
81+
builder.addLabel("appengine.googleapis.com/instance_name", gaeInstanceId);
82+
}
83+
String traceId = getCurrentTraceId();
84+
if (traceId != null) {
85+
builder.addLabel("appengine.googleapis.com/trace_id", traceId);
86+
}
87+
}
88+
}

google-cloud-logging/src/main/java/com/google/cloud/logging/LoggingHandler.java

Lines changed: 74 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@
2121
import com.google.cloud.MonitoredResource;
2222
import com.google.cloud.logging.Logging.WriteOption;
2323
import com.google.common.collect.ImmutableList;
24-
import com.google.common.collect.ImmutableMap;
24+
25+
import java.util.ArrayList;
26+
import java.util.Collections;
2527
import java.util.LinkedList;
2628
import java.util.List;
2729
import java.util.logging.ErrorManager;
@@ -76,6 +78,11 @@
7678
* <li>{@code com.google.cloud.logging.LoggingHandler.flushLevel} specifies the flush log level.
7779
* When a log with this level is published, logs are transmitted to the Stackdriver Logging
7880
* service (defaults to {@link LoggingLevel#ERROR}).
81+
* <li>{@code com.google.cloud.logging.LoggingHandler.enhancers} specifies a comma separated list
82+
* of {@link Enhancer} classes. This handler will call each enhancer list whenever it builds
83+
* a {@link MonitoredResource} or {@link LogEntry} instance (defaults to empty list).
84+
* <li>{@code com.google.cloud.logging.LoggingHandler.resourceType} the type name to use when
85+
* creating the default {@link MonitoredResource} (defaults to "global").
7986
* </ul>
8087
*
8188
* <p>To add a {@code LoggingHandler} to an existing {@link Logger} and be sure to avoid infinite
@@ -99,6 +106,7 @@ public class LoggingHandler extends Handler {
99106
private volatile Logging logging;
100107
private Level flushLevel;
101108
private long flushSize;
109+
private final List<Enhancer> enhancers;
102110

103111
/**
104112
* Creates an handler that publishes messages to Stackdriver Logging.
@@ -131,9 +139,30 @@ public LoggingHandler(String log, LoggingOptions options) {
131139
*
132140
* @param log the name of the log to which log entries are written
133141
* @param options options for the Stackdriver Logging service
134-
* @param monitoredResource the monitored resource to which log entries refer
142+
* @param monitoredResource the monitored resource to which log entries refer. If it is null
143+
* then a default resource is created based on the project ID. When creating a default resource, if
144+
* any {@link Enhancer} instances are configured and then each
145+
* {@link Enhancer#enhanceMonitoredResource(com.google.cloud.MonitoredResource.Builder)} method
146+
* is called before building the default resource.
135147
*/
136148
public LoggingHandler(String log, LoggingOptions options, MonitoredResource monitoredResource) {
149+
this(log, options, monitoredResource,null);
150+
}
151+
152+
/**
153+
* Creates a handler that publishes messages to Stackdriver Logging.
154+
*
155+
* @param log the name of the log to which log entries are written
156+
* @param options options for the Stackdriver Logging service
157+
* @param monitoredResource the monitored resource to which log entries refer. If it is null
158+
* then a default resource is created based on the project ID. When creating a default resource, if
159+
* any {@link Enhancer} instances are configured and then each
160+
* {@link Enhancer#enhanceMonitoredResource(com.google.cloud.MonitoredResource.Builder)} method
161+
* is called before building the default resource.
162+
* @param enhancers List of {@link Enhancer} instances used to enhance any {@link MonitoredResource}
163+
* or {@link LogEntry} instances built by this handler.
164+
*/
165+
public LoggingHandler(String log, LoggingOptions options, MonitoredResource monitoredResource, List<Enhancer> enhancers) {
137166
LogConfigHelper helper = new LogConfigHelper();
138167
String className = getClass().getName();
139168
this.options = options != null ? options : LoggingOptions.getDefaultInstance();
@@ -143,7 +172,9 @@ public LoggingHandler(String log, LoggingOptions options, MonitoredResource moni
143172
setFilter(helper.getFilterProperty(className + ".filter", null));
144173
setFormatter(helper.getFormatterProperty(className + ".formatter", new SimpleFormatter()));
145174
String logName = firstNonNull(log, helper.getProperty(className + ".log", "java.log"));
146-
MonitoredResource resource = firstNonNull(monitoredResource, getDefaultResource());
175+
this.enhancers = enhancers != null ? enhancers : helper.getEnhancerProperty(className + ".enhancers");
176+
String resourceType = helper.getProperty(className + ".resourceType", "global");
177+
MonitoredResource resource = monitoredResource != null ? monitoredResource : getDefaultResource(resourceType);
147178
writeOptions = new WriteOption[]{WriteOption.logName(logName), WriteOption.resource(resource)};
148179
}
149180

@@ -178,8 +209,13 @@ private static boolean hasLoggingHandler(Logger logger) {
178209
return false;
179210
}
180211

181-
private MonitoredResource getDefaultResource() {
182-
return MonitoredResource.of("global", ImmutableMap.of("project_id", options.getProjectId()));
212+
private MonitoredResource getDefaultResource(String resourceType) {
213+
MonitoredResource.Builder builder = MonitoredResource.newBuilder(resourceType);
214+
builder.addLabel("project_id", options.getProjectId());
215+
for (Enhancer enhancer : enhancers) {
216+
enhancer.enhanceMonitoredResource(builder);
217+
}
218+
return builder.build();
183219
}
184220

185221
private static class LogConfigHelper {
@@ -237,6 +273,24 @@ Formatter getFormatterProperty(String name, Formatter defaultValue) {
237273
}
238274
return defaultValue;
239275
}
276+
277+
List<Enhancer> getEnhancerProperty(String name) {
278+
String list = manager.getProperty(name);
279+
try {
280+
List<Enhancer> enhancers = new ArrayList<>();
281+
if (list != null) {
282+
String[] items = list.split(",");
283+
for (String e_name : items) {
284+
Class<? extends Enhancer> clz = (Class<? extends Enhancer>) ClassLoader.getSystemClassLoader().loadClass(e_name);
285+
enhancers.add((Enhancer) clz.newInstance());
286+
}
287+
}
288+
return enhancers;
289+
} catch (Exception ex) {
290+
// If we cannot create the enhancers we fall back to the default
291+
}
292+
return Collections.emptyList();
293+
}
240294
}
241295

242296
/**
@@ -311,12 +365,16 @@ private LogEntry entryFor(LogRecord record) {
311365
.addLabel("levelValue", String.valueOf(level.intValue()))
312366
.setTimestamp(record.getMillis())
313367
.setSeverity(severityFor(level));
368+
369+
for (Enhancer enhancer : enhancers) {
370+
enhancer.enhanceLogEntry(builder, record);
371+
}
314372
enhanceLogEntry(builder, record);
315373
return builder.build();
316374
}
317375

376+
@Deprecated
318377
protected void enhanceLogEntry(LogEntry.Builder builder, LogRecord record) {
319-
// no-op in this class
320378
}
321379

322380
private static Severity severityFor(Level level) {
@@ -430,4 +488,14 @@ public synchronized long setFlushSize(long flushSize) {
430488
public static void addHandler(Logger logger, LoggingHandler handler) {
431489
logger.addHandler(handler);
432490
}
491+
492+
/**
493+
* A Log Enhancer.
494+
* May be used to enhance the {@link MonitoredResource} and/or the {@link LogEntry}
495+
*/
496+
interface Enhancer {
497+
void enhanceMonitoredResource(MonitoredResource.Builder builder);
498+
void enhanceLogEntry(LogEntry.Builder builder, LogRecord record);
499+
}
500+
433501
}

google-cloud-logging/src/test/java/com/google/cloud/logging/LoggingHandlerTest.java

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,14 @@
1717
package com.google.cloud.logging;
1818

1919
import com.google.cloud.MonitoredResource;
20+
import com.google.cloud.logging.LogEntry.Builder;
2021
import com.google.cloud.logging.Logging.WriteOption;
2122
import com.google.cloud.logging.Payload.StringPayload;
2223
import com.google.common.collect.ImmutableList;
2324
import com.google.common.collect.ImmutableMap;
2425
import com.google.common.util.concurrent.Futures;
26+
27+
import java.util.Collections;
2528
import java.util.logging.ErrorManager;
2629
import java.util.logging.Formatter;
2730
import java.util.logging.Handler;
@@ -46,6 +49,13 @@ public class LoggingHandlerTest {
4649
.addLabel("levelValue", String.valueOf(Level.FINEST.intValue()))
4750
.setTimestamp(123456789L)
4851
.build();
52+
private static final LogEntry FINEST_ENHANCED_ENTRY = LogEntry.newBuilder(StringPayload.of(MESSAGE))
53+
.setSeverity(Severity.DEBUG)
54+
.addLabel("levelName", "FINEST")
55+
.addLabel("levelValue", String.valueOf(Level.FINEST.intValue()))
56+
.addLabel("enhanced", "true")
57+
.setTimestamp(123456789L)
58+
.build();
4959
private static final LogEntry FINER_ENTRY = LogEntry.newBuilder(StringPayload.of(MESSAGE))
5060
.setSeverity(Severity.DEBUG)
5161
.addLabel("levelName", "FINER")
@@ -227,6 +237,33 @@ public void testPublishCustomResource() {
227237
handler.publish(newLogRecord(Level.FINEST, MESSAGE));
228238
}
229239

240+
@Test
241+
public void testEnhancedLogEntry() {
242+
EasyMock.expect(options.getProjectId()).andReturn(PROJECT).anyTimes();
243+
EasyMock.expect(options.getService()).andReturn(logging);
244+
MonitoredResource resource = MonitoredResource.of("custom", ImmutableMap.<String, String>of());
245+
logging.writeAsync(ImmutableList.of(FINEST_ENHANCED_ENTRY), WriteOption.logName(LOG_NAME),
246+
WriteOption.resource(resource));
247+
EasyMock.expectLastCall().andReturn(Futures.immediateFuture(null));
248+
EasyMock.replay(options, logging);
249+
LoggingHandler.Enhancer enhancer = new LoggingHandler.Enhancer() {
250+
@Override
251+
public void enhanceMonitoredResource(MonitoredResource.Builder builder) {
252+
throw new IllegalStateException();
253+
}
254+
255+
@Override
256+
public void enhanceLogEntry(Builder builder, LogRecord record) {
257+
builder.addLabel("enhanced", "true");
258+
}
259+
};
260+
Handler handler =
261+
new LoggingHandler(LOG_NAME, options, resource, Collections.singletonList(enhancer));
262+
handler.setLevel(Level.ALL);
263+
handler.setFormatter(new TestFormatter());
264+
handler.publish(newLogRecord(Level.FINEST, MESSAGE));
265+
}
266+
230267
@Test
231268
public void testReportFlushError() {
232269
EasyMock.expect(options.getProjectId()).andReturn(PROJECT).anyTimes();

0 commit comments

Comments
 (0)