All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.kdgregory.log4j2.aws.CloudWatchAppender Maven / Gradle / Ivy

There is a newer version: 3.2.1
Show newest version
// Copyright (c) Keith D Gregory
//
// 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.kdgregory.log4j2.aws;

import java.util.Date;

import org.apache.logging.log4j.core.Appender;
import org.apache.logging.log4j.core.Core;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
import org.apache.logging.log4j.core.config.plugins.validation.constraints.Required;
import org.apache.logging.log4j.core.lookup.StrSubstitutor;

import com.kdgregory.log4j2.aws.internal.AbstractAppender;
import com.kdgregory.log4j2.aws.internal.AbstractAppenderBuilder;
import com.kdgregory.log4j2.aws.internal.CloudWatchAppenderConfig;
import com.kdgregory.logging.aws.cloudwatch.CloudWatchConstants;
import com.kdgregory.logging.aws.cloudwatch.CloudWatchWriterConfig;
import com.kdgregory.logging.aws.cloudwatch.CloudWatchWriterFactory;
import com.kdgregory.logging.aws.cloudwatch.CloudWatchWriterStatistics;
import com.kdgregory.logging.aws.cloudwatch.CloudWatchWriterStatisticsMXBean;
import com.kdgregory.logging.aws.common.Substitutions;
import com.kdgregory.logging.common.util.DefaultThreadFactory;
import com.kdgregory.logging.common.util.InternalLogger;


/**
 *  An appender that writes to a CloudWatch log stream.
 *  

* This appender supports the following configuration parameters: *

*

* * * * * * * * * * * * * * *
logGroup * Name of the CloudWatch log group where messages are sent; may use * substitutions. If this group doesn't exist it will be created. *

* You typically assign a single log group to an application, and then * use multiple log streams for instances of that application. *

* There is no default value. If you do not configure the log group, * the appender will be disabled and will report its misconfiguration. * *

logStream * Name of the CloudWatch log stream where messages are sent; may use * substitutions. If this stream doesn't exist it will be created. *

* You typically create a separate log stream for each instance of * the application. *

* Default value is {startTimestamp}, the JVM startup * timestamp. * *

retentionPeriod * (optional) Specifies a retention period for created CloudWatch log * groups. If omitted, messages are retained forever. Note that values * are restricted; see {@link AWS API doc}. * *
dedicatedWriter * If true (the default), the appender assumes that it is the only thing * writing to the log stream, and does not fetch a sequence token before * each write. This improves performance and reduces the likelihood of * throttling when there are a large number of processes (as long as they * write to different streams). If you need to have multiple appenders * writing to the same stream, set this to false. * *
synchronous * If true, the appender will operate in synchronous mode: calls to * append() execute on the caller's thread, and will not return until * writer has attempted to send a batch (errors may still result in * messages being requeued for the next batch). This negates the benefits * of message batching, and should only be used in cases (like AWS Lambda) * where background thread processing is not guaranteed. *

* Note: setting synchronous to true also sets * batchDelay to 0. * *

batchDelay * The time, in milliseconds, that the writer will wait to accumulate * messages for a batch. *

* The writer attempts to gather multiple logging messages into a batch, * to reduce communication with the service. The batch delay controls * the time that a message will remain in-memory while the writer builds * this batch. In a low-volume environment it will be the main determinant * of when the batch is sent; in a high volume environment it's likely * that the maximum request size will be reached before the delay elapses. *

* The default value is 2000, which is rather arbitrarily chosen. *

* If the appender is in synchronous mode, this setting is ignored. * *

truncateOversizeMessages * If true (the default), oversize messages are truncated to * the maximum length permitted by CloudWatch Logs. If false * they are discarded. In either case, the oversized message is reported * to the Log4J debug log. * *
discardThreshold * The number of unsent messages that will trigger message discard. A * high value is useful when network connectivity is intermittent and/or * overall AWS communication is causing throttling. However, a value that * is too high may cause out-of-memory errors. *

* The default, 10,000, is based on the assumptions that (1) each message * will be 1k or less, and (2) any app that uses remote logging can afford * 10MB. * *

discardAction * The action to take when the number of unsent messages exceeds the * discard threshold. Values are "none" (retain all messages), "oldest" * (discard oldest messages), and "newest" (discard most recent messages). *

* The default is "oldest". Attempting to set an incorrect value will throw * a configuration error. * *

assumedRole * Specifies role name or ARN that will be assumed by this appender. Useful * for cross-account logging. If the appender does not have permission to * assume this role, initialization will fail. * *
clientFactory * The fully-qualified name of a static method to create the correct AWS * client, which will be called instead of the writer's internal client * factory. This is useful if you need non-default configuration, such as * using a proxy server. *

* The passed string is of the form com.example.Classname.methodName. * If this does not reference a class/method on the classpath then writer * initialization will fail. * *

clientRegion * Specifies a non-default service region. This setting is ignored if you * use a client factory. * *
clientEndpoint * Specifies a non-default service endpoint. Typically used when running in * a VPC, when the normal endpoint is not available. * *
useShutdownHook * This exists for consistency with other appenders but ignored; Log4J2 provides * its own shutdown hooks. *
* * @see Appender documentation */ @Plugin(name = "CloudWatchAppender", category = Core.CATEGORY_NAME, elementType = Appender.ELEMENT_TYPE) public class CloudWatchAppender extends AbstractAppender < CloudWatchWriterConfig, CloudWatchAppenderConfig, CloudWatchWriterStatistics, CloudWatchWriterStatisticsMXBean > { //---------------------------------------------------------------------------- // Builder //---------------------------------------------------------------------------- @PluginBuilderFactory public static CloudWatchAppenderBuilder newBuilder() { return new CloudWatchAppenderBuilder(); } public static class CloudWatchAppenderBuilder extends AbstractAppenderBuilder implements CloudWatchAppenderConfig, org.apache.logging.log4j.core.util.Builder { @PluginBuilderAttribute("name") @Required(message = "CloudWatchAppender: no name provided") private String name; @Override public String getName() { return name; } public CloudWatchAppenderBuilder setName(String value) { this.name = value; return this; } @PluginBuilderAttribute("logGroup") private String logGroup; /** * Sets the logGroup configuration property. */ public CloudWatchAppenderBuilder setLogGroup(String value) { this.logGroup = value; return this; } /** * Returns the logGroup configuration property. */ @Override public String getLogGroup() { return logGroup; } @PluginBuilderAttribute("logStream") private String logStream = "{startupTimestamp}"; /** * Sets the logStream configuration property. */ public CloudWatchAppenderBuilder setLogStream(String value) { this.logStream = value; return this; } /** * Returns the logStream configuration property. */ @Override public String getLogStream() { return logStream; } @PluginBuilderAttribute("retentionPeriod") private Integer retentionPeriod; /** * Sets the retentionPeriod configuration property. */ public CloudWatchAppenderBuilder setRetentionPeriod(Integer value) { // note: validation happens in appender because Log4J bypasses // this setter during normal configuration this.retentionPeriod = value; return this; } /** * Returns the retentionPeriod configuration property, * null to indicate unlimited retention. */ @Override public Integer getRetentionPeriod() { return retentionPeriod; } @PluginBuilderAttribute("dedicatedWriter") private boolean dedicatedWriter = true; /** * Sets the dedicatedWriter configuration property. */ public CloudWatchAppenderBuilder setDedicatedWriter(boolean value) { this.dedicatedWriter = value; return this; } /** * Returns the dedicatedWriter configuration property. */ @Override public boolean isDedicatedWriter() { return dedicatedWriter; } @Override public CloudWatchAppender build() { return new CloudWatchAppender(name, this, null); } } //---------------------------------------------------------------------------- // Appender //---------------------------------------------------------------------------- // this is extracted from config so that we can validate protected Integer retentionPeriod; protected CloudWatchAppender(String name, CloudWatchAppenderConfig config, InternalLogger internalLogger) { super( name, new DefaultThreadFactory("log4j2-cloudwatch"), new CloudWatchWriterFactory(), new CloudWatchWriterStatistics(), CloudWatchWriterStatisticsMXBean.class, config, internalLogger); try { retentionPeriod = CloudWatchConstants.validateRetentionPeriod(config.getRetentionPeriod()); } catch (IllegalArgumentException ex) { internalLogger.error(ex.getMessage(), null); } } //---------------------------------------------------------------------------- // Internals //---------------------------------------------------------------------------- @Override protected CloudWatchWriterConfig generateWriterConfig() { StrSubstitutor l4jsubs = appenderConfig.getConfiguration().getStrSubstitutor(); Substitutions subs = new Substitutions(new Date(), 0); String actualLogGroup = subs.perform(l4jsubs.replace(appenderConfig.getLogGroup())); String actualLogStream = subs.perform(l4jsubs.replace(appenderConfig.getLogStream())); return new CloudWatchWriterConfig() .setLogGroupName(actualLogGroup) .setLogStreamName(actualLogStream) .setRetentionPeriod(retentionPeriod) .setDedicatedWriter(appenderConfig.isDedicatedWriter()); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy