Skip to content

Commit

Permalink
Initialize the default MonitoredResource from a GAE environment (#1535)
Browse files Browse the repository at this point in the history
  • Loading branch information
gregw authored and garrettjonesgoogle committed Jan 19, 2017
1 parent 0010dd8 commit 34c186f
Show file tree
Hide file tree
Showing 3 changed files with 199 additions and 6 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
* Copyright 2016 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.google.cloud.logging;

import java.util.logging.LogRecord;

import com.google.cloud.MonitoredResource.Builder;

/**
* A Logging {@link Enhancer} that enhances the logging for the
* GAE Flex environment. This enhancer can
* be configured in a logging.properties file with:
*
* <pre>
* handlers=com.google.cloud.logging.LoggingHandler
* com.google.cloud.logging.LoggingHandler.log=gaeflex.log
* com.google.cloud.logging.LoggingHandler.resourceType=gae_app
* com.google.cloud.logging.LoggingHandler.enhancers=com.google.cloud.logging.GaeFlexLoggingEnhancer
* com.google.cloud.logging.LoggingHandler.formatter = java.util.logging.SimpleFormatter
* java.util.logging.SimpleFormatter.format=%3$s: %5$s%6$s
* </pre>
*
*/
public class GaeFlexLoggingEnhancer implements LoggingHandler.Enhancer {

private static final ThreadLocal<String> traceId = new ThreadLocal<>();

private final String gaeInstanceId;

/**
* Set the Trace ID associated with any logging done by the current thread.
*
* @param id The traceID
*/
public static void setCurrentTraceId(String id) {
traceId.set(id);
}

/**
* Get the Trace ID associated with any logging done by the current thread.
*
* @return id The traceID
*/
public static String getCurrentTraceId() {
return traceId.get();
}

public GaeFlexLoggingEnhancer() {
gaeInstanceId = System.getenv("GAE_INSTANCE"); // Are we running on a GAE instance?
}

@Override
public void enhanceMonitoredResource(Builder builder) {
if (gaeInstanceId != null) {
if (System.getenv("GAE_SERVICE") != null) {
builder.addLabel("module_id", System.getenv("GAE_SERVICE"));
}
if (System.getenv("GAE_VERSION") != null) {
builder.addLabel("version_id", System.getenv("GAE_VERSION"));
}
}
}

@Override
public void enhanceLogEntry(com.google.cloud.logging.LogEntry.Builder builder, LogRecord record) {
if (gaeInstanceId != null) {
builder.addLabel("appengine.googleapis.com/instance_name", gaeInstanceId);
}
String traceId = getCurrentTraceId();
if (traceId != null) {
builder.addLabel("appengine.googleapis.com/trace_id", traceId);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@
import com.google.cloud.MonitoredResource;
import com.google.cloud.logging.Logging.WriteOption;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;

import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.ErrorManager;
Expand Down Expand Up @@ -76,6 +78,11 @@
* <li>{@code com.google.cloud.logging.LoggingHandler.flushLevel} specifies the flush log level.
* When a log with this level is published, logs are transmitted to the Stackdriver Logging
* service (defaults to {@link LoggingLevel#ERROR}).
* <li>{@code com.google.cloud.logging.LoggingHandler.enhancers} specifies a comma separated list
* of {@link Enhancer} classes. This handler will call each enhancer list whenever it builds
* a {@link MonitoredResource} or {@link LogEntry} instance (defaults to empty list).
* <li>{@code com.google.cloud.logging.LoggingHandler.resourceType} the type name to use when
* creating the default {@link MonitoredResource} (defaults to "global").
* </ul>
*
* <p>To add a {@code LoggingHandler} to an existing {@link Logger} and be sure to avoid infinite
Expand All @@ -99,6 +106,7 @@ public class LoggingHandler extends Handler {
private volatile Logging logging;
private Level flushLevel;
private long flushSize;
private final List<Enhancer> enhancers;

/**
* Creates an handler that publishes messages to Stackdriver Logging.
Expand Down Expand Up @@ -131,9 +139,30 @@ public LoggingHandler(String log, LoggingOptions options) {
*
* @param log the name of the log to which log entries are written
* @param options options for the Stackdriver Logging service
* @param monitoredResource the monitored resource to which log entries refer
* @param monitoredResource the monitored resource to which log entries refer. If it is null
* then a default resource is created based on the project ID. When creating a default resource, if
* any {@link Enhancer} instances are configured and then each
* {@link Enhancer#enhanceMonitoredResource(com.google.cloud.MonitoredResource.Builder)} method
* is called before building the default resource.
*/
public LoggingHandler(String log, LoggingOptions options, MonitoredResource monitoredResource) {
this(log, options, monitoredResource,null);
}

/**
* Creates a handler that publishes messages to Stackdriver Logging.
*
* @param log the name of the log to which log entries are written
* @param options options for the Stackdriver Logging service
* @param monitoredResource the monitored resource to which log entries refer. If it is null
* then a default resource is created based on the project ID. When creating a default resource, if
* any {@link Enhancer} instances are configured and then each
* {@link Enhancer#enhanceMonitoredResource(com.google.cloud.MonitoredResource.Builder)} method
* is called before building the default resource.
* @param enhancers List of {@link Enhancer} instances used to enhance any {@link MonitoredResource}
* or {@link LogEntry} instances built by this handler.
*/
public LoggingHandler(String log, LoggingOptions options, MonitoredResource monitoredResource, List<Enhancer> enhancers) {
LogConfigHelper helper = new LogConfigHelper();
String className = getClass().getName();
this.options = options != null ? options : LoggingOptions.getDefaultInstance();
Expand All @@ -143,7 +172,9 @@ public LoggingHandler(String log, LoggingOptions options, MonitoredResource moni
setFilter(helper.getFilterProperty(className + ".filter", null));
setFormatter(helper.getFormatterProperty(className + ".formatter", new SimpleFormatter()));
String logName = firstNonNull(log, helper.getProperty(className + ".log", "java.log"));
MonitoredResource resource = firstNonNull(monitoredResource, getDefaultResource());
this.enhancers = enhancers != null ? enhancers : helper.getEnhancerProperty(className + ".enhancers");
String resourceType = helper.getProperty(className + ".resourceType", "global");
MonitoredResource resource = monitoredResource != null ? monitoredResource : getDefaultResource(resourceType);
writeOptions = new WriteOption[]{WriteOption.logName(logName), WriteOption.resource(resource)};
}

Expand Down Expand Up @@ -178,8 +209,13 @@ private static boolean hasLoggingHandler(Logger logger) {
return false;
}

private MonitoredResource getDefaultResource() {
return MonitoredResource.of("global", ImmutableMap.of("project_id", options.getProjectId()));
private MonitoredResource getDefaultResource(String resourceType) {
MonitoredResource.Builder builder = MonitoredResource.newBuilder(resourceType);
builder.addLabel("project_id", options.getProjectId());
for (Enhancer enhancer : enhancers) {
enhancer.enhanceMonitoredResource(builder);
}
return builder.build();
}

private static class LogConfigHelper {
Expand Down Expand Up @@ -237,6 +273,24 @@ Formatter getFormatterProperty(String name, Formatter defaultValue) {
}
return defaultValue;
}

List<Enhancer> getEnhancerProperty(String name) {
String list = manager.getProperty(name);
try {
List<Enhancer> enhancers = new ArrayList<>();
if (list != null) {
String[] items = list.split(",");
for (String e_name : items) {
Class<? extends Enhancer> clz = (Class<? extends Enhancer>) ClassLoader.getSystemClassLoader().loadClass(e_name);
enhancers.add((Enhancer) clz.newInstance());
}
}
return enhancers;
} catch (Exception ex) {
// If we cannot create the enhancers we fall back to the default
}
return Collections.emptyList();
}
}

/**
Expand Down Expand Up @@ -311,12 +365,16 @@ private LogEntry entryFor(LogRecord record) {
.addLabel("levelValue", String.valueOf(level.intValue()))
.setTimestamp(record.getMillis())
.setSeverity(severityFor(level));

for (Enhancer enhancer : enhancers) {
enhancer.enhanceLogEntry(builder, record);
}
enhanceLogEntry(builder, record);
return builder.build();
}

@Deprecated
protected void enhanceLogEntry(LogEntry.Builder builder, LogRecord record) {
// no-op in this class
}

private static Severity severityFor(Level level) {
Expand Down Expand Up @@ -430,4 +488,14 @@ public synchronized long setFlushSize(long flushSize) {
public static void addHandler(Logger logger, LoggingHandler handler) {
logger.addHandler(handler);
}

/**
* A Log Enhancer.
* May be used to enhance the {@link MonitoredResource} and/or the {@link LogEntry}
*/
interface Enhancer {
void enhanceMonitoredResource(MonitoredResource.Builder builder);
void enhanceLogEntry(LogEntry.Builder builder, LogRecord record);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,14 @@
package com.google.cloud.logging;

import com.google.cloud.MonitoredResource;
import com.google.cloud.logging.LogEntry.Builder;
import com.google.cloud.logging.Logging.WriteOption;
import com.google.cloud.logging.Payload.StringPayload;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.util.concurrent.Futures;

import java.util.Collections;
import java.util.logging.ErrorManager;
import java.util.logging.Formatter;
import java.util.logging.Handler;
Expand All @@ -46,6 +49,13 @@ public class LoggingHandlerTest {
.addLabel("levelValue", String.valueOf(Level.FINEST.intValue()))
.setTimestamp(123456789L)
.build();
private static final LogEntry FINEST_ENHANCED_ENTRY = LogEntry.newBuilder(StringPayload.of(MESSAGE))
.setSeverity(Severity.DEBUG)
.addLabel("levelName", "FINEST")
.addLabel("levelValue", String.valueOf(Level.FINEST.intValue()))
.addLabel("enhanced", "true")
.setTimestamp(123456789L)
.build();
private static final LogEntry FINER_ENTRY = LogEntry.newBuilder(StringPayload.of(MESSAGE))
.setSeverity(Severity.DEBUG)
.addLabel("levelName", "FINER")
Expand Down Expand Up @@ -227,6 +237,33 @@ public void testPublishCustomResource() {
handler.publish(newLogRecord(Level.FINEST, MESSAGE));
}

@Test
public void testEnhancedLogEntry() {
EasyMock.expect(options.getProjectId()).andReturn(PROJECT).anyTimes();
EasyMock.expect(options.getService()).andReturn(logging);
MonitoredResource resource = MonitoredResource.of("custom", ImmutableMap.<String, String>of());
logging.writeAsync(ImmutableList.of(FINEST_ENHANCED_ENTRY), WriteOption.logName(LOG_NAME),
WriteOption.resource(resource));
EasyMock.expectLastCall().andReturn(Futures.immediateFuture(null));
EasyMock.replay(options, logging);
LoggingHandler.Enhancer enhancer = new LoggingHandler.Enhancer() {
@Override
public void enhanceMonitoredResource(MonitoredResource.Builder builder) {
throw new IllegalStateException();
}

@Override
public void enhanceLogEntry(Builder builder, LogRecord record) {
builder.addLabel("enhanced", "true");
}
};
Handler handler =
new LoggingHandler(LOG_NAME, options, resource, Collections.singletonList(enhancer));
handler.setLevel(Level.ALL);
handler.setFormatter(new TestFormatter());
handler.publish(newLogRecord(Level.FINEST, MESSAGE));
}

@Test
public void testReportFlushError() {
EasyMock.expect(options.getProjectId()).andReturn(PROJECT).anyTimes();
Expand Down

0 comments on commit 34c186f

Please sign in to comment.