Skip to main content

Java

tip

For Kubernetes data, use the dedicated integration.

Logs

The Logz.io Log4j 2 appender sends logs via non-blocking threading bulks and HTTPS encryption to port 8071. It uses LogzioSender, with logs queued in a buffer and are 100% non-blocking, shipped by a background task. This .jar includes LogzioSender, BigQueue, Gson, and Guava.

Requirements::

  • Log4j 2.7+
  • Java 8+

Add a dependency to a configuration file

JDK 8:

    <dependency>
<groupId>io.logz.log4j2</groupId>
<artifactId>logzio-log4j2-appender</artifactId>
<version>1.0.19</version>
</dependency>
note

If you encounter any issue, try using version 1.0.12 or earlier.

JDK 11+:

    <dependency>
<groupId>io.logz.log4j2</groupId>
<artifactId>logzio-log4j2-appender</artifactId>
<version>2.0.1</version>
</dependency>

The appender also requires a logger implementation:

    <dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.15.0</version>
</dependency>

Find the logzio-log4j2-appender artifact in the Maven central repo.

Appender configuration

Replace the placeholders with your configuration.

XML example:

<Appenders>

<LogzioAppender name="Logzio">
<logzioToken><<LOG-SHIPPING-TOKEN>></logzioToken>
<logzioUrl>https://<<LISTENER-HOST>>:8071</logzioUrl>
<logzioType>myAwesomeType</logzioType>
</LogzioAppender>

</Appenders>

<Loggers>
<Root level="info">
<AppenderRef ref="Logzio"/>
</Root>
</Loggers>

log4j2.properties example:

# Extra logging related to initialization of Log4j
# Set to debug or trace if log4j initialization is failing
status = debug
# Name of the configuration
name = io.logz.log4j2

appenders=logzioAppender

# Logz.io configuration
appender.logzioAppender.type = logzioAppender
appender.logzioAppender.name = Logzio
appender.logzioAppender.LogzioToken = <<LOG-SHIPPING-TOKEN>>
appender.logzioAppender.LogzioType = myAwesomeType
appender.logzioAppender.LogzioUrl = https://<<LISTENER-HOST>>:8071

# Root logger level
rootLogger.level = debug
# Root logger referring to logzio appender
rootLogger.appenderRef.logzioAppender.ref = logzioAppender
note

For more details, see the Log4j documentation.

Appender parameters

ParameterDefaultExplainedRequired/Optional
logzioTokenNoneYour Logz.io log shipping token. Replace <<LOG-SHIPPING-TOKEN>> with the token of the account you want to ship to. Begin with $ to use an environment variable or system property with the specified name. For example, $LOGZIO_TOKEN uses the LOGZIO_TOKEN environment variable.Required
logzioTypejavaThe log type. Can't contain spaces.Optional
logzioUrlhttps://listener.logz.io:8071Listener URL and port. Replace <<LISTENER-HOST>> with the host for your region. The required port depends whether HTTP or HTTPS is used: HTTP = 8070, HTTPS = 8071.Required
drainTimeoutSec5How often the appender drains the buffer, in seconds.Required
socketTimeoutMs10 1000*Socket timeout during log shipment.Required
connectTimeoutMs10 1000*Connection timeout during log shipment, in milliseconds.Required
addHostnamefalseIf true, adds a field named hostname with the machine's hostname. If there's no defined hostname, the field won't be added.Required
additionalFieldsNoneAllows to add additional fields to the JSON message sent. The format is "fieldName1=fieldValue1;fieldName2=fieldValue2". Optionally, inject an environment variable value using this format: "fieldName1=fieldValue1;fieldName2=$ENV_VAR_NAME". The environment variable should be the only value. If the environment variable can't be resolved, the field will be omitted.Optional
debugfalseBoolean. Set to true to print debug messages to stdout.Required
compressRequestsfalseBoolean. If true, logs are compressed in gzip format before sending. If false, logs are sent uncompressed.Required
exceedMaxSizeAction"cut"String. Use "cut" to truncate the message or "drop" to discard oversized logs. Logs exceeding the maximum size after truncation will be dropped.Required

In-memory queue parameters

ParameterDefaultExplained
inMemoryQueueCapacityBytes1024 1024 100Memory (in bytes) allowed to use for the memory queue. -1 value means no limit.
inMemoryLogsCountCapacity-1Number of logs allowed in the queue before dropping logs. -1 value means no limit.
inMemoryQueuefalseSet to true to use in memory queue. Default is disk queue.

Disk queue parameters

ParameterDefaultExplained
fileSystemFullPercentThreshold98Percentage of file system usage at which the sender stops queueing. Once reached, new logs are dropped until usage falls below the threshold. Set to -1 to never stop processing logs.
gcPersistedQueueFilesIntervalSeconds30Interval (in seconds) for cleaning sent logs from disk.
bufferDir(deprecated, use queueDir)System.getProperty("java.io.tmpdir")Directory for storing the queue.
queueDirSystem.getProperty("java.io.tmpdir")Directory for storing the queue.

Code Example:

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class LogzioLog4j2Example {
public static void main(String[] args) {
Logger logger = LogManager.getLogger(LogzioLog4j2Example.class);

logger.info("Testing logz.io!");
logger.warn("Winter is coming");
}
}

Troubleshooting

If you receive an error about a missing appender, add the following to the configuration file:


<Configuration status="info" packages="io.logz.log4j2">

# Place the configuration from step 2

</Configuration>

Using Mapped Diagnostic Context (MDC)

Add MDC with the following code:

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.ThreadContext;

public class LogzioLog4j2Example {
public static void main(String[] args) {
Logger logger = LogManager.getLogger(LogzioLog4j2Example.class);
ThreadContext.put("Key", "Value");
logger.info("This log will hold the MDC data as well");
}
}

Which produces the following output:

{
"message": "This log will hold the MDC data as well",
"Key": "Value",
"Your log message follows": "..."
}

Using Markers

Markers are used to tag and enrich log statements. Add them by running this:

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.MarkerManager;

public class LogzioLog4j2Example {
public static void main(String[] args) {
Logger logger = LogManager.getLogger(LogzioLog4j2Example.class);
Marker marker = MarkerManager.getMarker("Fatal");
logger.error(marker, "This line has a fatal error");
}
}

Which produces the following output:

{
"message": "This line has a fatal error",
"Marker": "Fatal",
"Your log message follows": "..."
}

Metrics

Usage

<dependency>
<groupId>io.logz.micrometer</groupId>
<artifactId>micrometer-registry-logzio</artifactId>
<version>1.0.2</version>
</dependency>

Import to your code

import io.micrometer.logzio.LogzioConfig;
import io.micrometer.logzio.LogzioMeterRegistry;

Getting started

Replace the placeholders in the code (indicated by << >>) to match your specifics.

Environment variableDescriptionRequired/Default
<<LISTENER-HOST>>Logz.io Listener URL for your region. Port 8052 for HTTP, or port 8053 for HTTPS. See the regions page for more info.Required
<<PROMETHEUS-METRICS-SHIPPING-TOKEN>>Logz.io Prometheus Metrics account token. Find it under Settings > Manage accounts. Look up your Metrics account token..Required
intervalInterval in seconds to push metrics to Logz.io. Your program must run for at least one interval.Required

Example:

package your_package;
import io.micrometer.core.instrument.*;
import io.micrometer.core.instrument.Timer;
import io.micrometer.logzio.LogzioConfig;
import io.micrometer.logzio.LogzioMeterRegistry;

class MicrometerLogzio {

public static void main(String[] args) {
// initilize config
LogzioConfig logzioConfig = new LogzioConfig() {
@Override
public String get(String key) {
return null;
}
@Override
public String uri() {
return "https://<<LISTENER-HOST>>":8053;
// example:
// return "https://listener.logz.io:8053";
}

@Override
public String token() {
return "<<PROMETHEUS-METRICS-SHIPPING-TOKEN>>";
}

@Override
public Duration step() {
return Duration.ofSeconds(<<interval>>);
// example:
// return Duration.ofSeconds(30);
}
@Override
public Hashtable<String, String> includeLabels() {
return new Hashtable<>();
}
@Override
public Hashtable<String, String> excludeLabels() {
return new Hashtable<>();
};
// Initialize registry
LogzioMeterRegistry registry = new LogzioMeterRegistry(logzioConfig, Clock.SYSTEM);
// Define tags (labels)
ArrayList<Tag> tags = new ArrayList<>();
tags.add(Tag.of("env","dev-micrometer"));

// Create counter
Counter counter = Counter
.builder("counter_example")
.description("a description of what this counter does") // optional
.tags(tags) // optional
.register(registry);
// Increment your counter
counter.increment();
counter.increment(2);
}
}

Configuring common tags

Attach common tags to your registry to include them in all reported metrics. For example:

// Initialize registry
LogzioMeterRegistry registry = new LogzioMeterRegistry(logzioConfig, Clock.SYSTEM);
// Define tags (labels)
registry.config().commonTags("key", "value");

Filtering labels - Include

Use includeLabels in your LogzioConfig() constructor:

@Override
public Hashtable<String, String> includeLabels() {
Hashtable<String, String> includeLabels = new Hashtable<>();
includeLabels.put("__name__", "my_counter_abc_total|my_second_counter_abc_total");
includeLabels.put("k1", "v1");
return includeLabels;
}

The registry will keep only metrics with the label __name__ matching the regex my_counter_abc_total|my_second_counter_abc_total, and with the label k1 matching the regex v1.

Filtering labels - Exclude

Use excludeLabels in your LogzioConfig() constructor:

@Override
public Hashtable<String, String> excludeLabels() {
Hashtable<String, String> excludeLabels = new Hashtable<>();
excludeLabels.put("__name__", "my_counter_abc_total|my_second_counter_abc_total");
excludeLabels.put("k1", "v1");
return excludeLabels;
}

The registry will drop all metrics with the label __name__ matching the regex my_counter_abc_total|my_second_counter_abc_total, and with the label k1 matching the regex v1.

Using meter binders

Micrometer provides a set of binders for monitoring JVM metrics out of the box:

// Initialize registry
LogzioMeterRegistry registry = new LogzioMeterRegistry(logzioConfig, Clock.SYSTEM);

// Gauges buffer and memory pool utilization
new JvmMemoryMetrics().bindTo(registry);
// Gauges max and live data size, promotion and allocation rates, and times GC pauses
new JvmGcMetrics().bindTo(registry);
// Gauges current CPU total and load average.
new ProcessorMetrics().bindTo(registry);
// Gauges thread peak, number of daemon threads, and live threads
new JvmThreadMetrics().bindTo(registry);
// Gauges loaded and unloaded classes
new ClassLoaderMetrics().bindTo(registry);

// File descriptor metrics gathered by the JVM
new FileDescriptorMetrics(tags).bindTo(registry);
// Gauges The uptime and start time of the Java virtual machine
new UptimeMetrics(tags).bindTo(registry);

// Counter of logging events
new LogbackMetrics().bindTo(registry);
new Log4j2Metrics().bindTo(registry);

For more information about other binders check out the Micrometer-core Github repo.

Metric types

NameBehavior
CounterMetric value can only go up or be reset to 0, calculated per counter.increment(value); call.
GaugeMetric value can arbitrarily increment or decrement, values can set automaticaly by tracking Collection size or manually by gauge.set(value).
DistributionSummaryCaptured metric values via summary.record(value). Outputs a distribution of count,sum and max for the recorded values during the push interval.
TimerMesures timing. Metric values recorded by timer.record() call.

For more details, see the Micrometer documentation.

Counter

Counter counter = Counter
.builder("counter_example")
.description("a description of what this counter does") // optional
.tags(tags) // optional
.register(registry);
// Increment your counter
counter.increment();
counter.increment(2);
// The following metric will be created and sent to Logz.io: counter_example_total{env="dev"} 3

Gauge

// Create Gauge
List<String> cache = new ArrayList<>(4);
// Track list size
Gauge gauge = Gauge
.builder("cache_size_gauge_example", cache, List::size)
.tags(tags)
.register(registry);
cache.add("1");
// The following metric will be created and sent to Logz.io: cache_size_gauge_example{env="dev"} 1

// Track map size
Map<String, Integer> map_gauge = registry.gaugeMapSize("map_gauge_example", tags, new HashMap<>());
map_gauge.put("key",1);
// The following metric will be created and sent to Logz.io: map_gauge_example{env="dev"} 1

// set value manually
AtomicInteger manual_gauge = registry.gauge("manual_gauge_example", new AtomicInteger(0));
manual_gauge.set(83);
// The following metric will be created and sent to Logz.io:: manual_gauge_example{env="dev"} 83

DistributionSummary

// Create DistributionSummary
DistributionSummary summary = DistributionSummary
.builder("summary_example")
.description("a description of what this summary does") // optional
.tags(tags) // optional
.register(registry);
// Record values to distributionSummary
summary.record(10);
summary.record(20);
summary.record(30);
// // The following metrics will be created and sent to Logz.io:
// summary_example_count{env="dev"} 3
// summary_example_max{env="dev"} 30
// summary_example_sum{env="dev"} 60

Timer

// Create Timer
Timer timer = Timer
.builder("timer_example")
.description("a description of what this timer does") // optional
.tags(tags) // optional
.register(registry);
// You can set a value manually
timer.record(1500,TimeUnit.MILLISECONDS);
// You can record the timing of a function
timer.record(()-> {
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
// The following metrics will be created and sent to Logz.io:
// timer_example_duration_seconds_count{env="dev"} 2
// timer_example_duration_seconds_max{env="dev"} 1501
// timer_example_duration_seconds_sum{env="dev"} 3000

Run your application

Run your application to start sending metrics to Logz.io. Give it some time to run and check the Logz.io Metrics dashboard.

Traces

Deploy this integration for automatic instrumentation of your Java application using OpenTelemetry. The Java agent captures spans and forwards them to the collector, which exports data to your Logz.io account.

This integration includes:

  • Downloading the OpenTelemetry Java agent to your application host
  • Installing the OpenTelemetry collector with Logz.io exporter
  • Establishing communication between the agent and collector

Requirements:

  • A Java application without instrumentation.
  • An active Logz.io account.
  • Port 4317 available on your host system.
  • A name defined for your tracing service. You will need it to identify the traces in Logz.io.
note

This integration uses OpenTelemetry Collector Contrib, not the OpenTelemetry Collector Core.

Setting up auto-instrumentation and sending Traces to Logz.io

1. Download Java agent

Download the latest version of the OpenTelemetry Java agent to your application host.

2. Download and configure OpenTelemetry collector

Create a dedicated directory on the host of your Java application and download the relevant OpenTelemetry collector.

Next, create a configuration file, config.yaml, with the following parameters:

receivers:
otlp:
protocols:
grpc:
endpoint: "0.0.0.0:4317"
http:
endpoint: "0.0.0.0:4318"

exporters:
logzio/traces:
account_token: "<<TRACING-SHIPPING-TOKEN>>"
region: "<<LOGZIO_ACCOUNT_REGION_CODE>>"
headers:
user-agent: logzio-opentelemetry-traces

logging:

processors:
batch:
tail_sampling:
policies:
[
{
name: policy-errors,
type: status_code,
status_code: {status_codes: [ERROR]}
},
{
name: policy-slow,
type: latency,
latency: {threshold_ms: 1000}
},
{
name: policy-random-ok,
type: probabilistic,
probabilistic: {sampling_percentage: 10}
}
]

extensions:
pprof:
endpoint: :1777
zpages:
endpoint: :55679
health_check:

service:
extensions: [health_check, pprof, zpages]
pipelines:
traces:
receivers: [otlp]
processors: [tail_sampling, batch]
exporters: [logging, logzio/traces]

Replace <<TRACING-SHIPPING-TOKEN>> with the token of the account you want to ship to.

Replace <LOGZIO_ACCOUNT_REGION_CODE> with the applicable region code.

3. Start the collector

Run:

<path/to>/otelcontribcol_<VERSION-NAME> --config ./config.yaml
  • Replace <path/to> with the collector's directory.
  • Replace <VERSION-NAME> with the version name, e.g. otelcontribcol_darwin_amd64.

4. Attach the agent

Run the following command from your Java application's directory:

java -javaagent:<path/to>/opentelemetry-javaagent-all.jar \
-Dotel.traces.exporter=otlp \
-Dotel.metrics.exporter=none \
-Dotel.resource.attributes=service.name=<YOUR-SERVICE-NAME> \
-Dotel.exporter.otlp.endpoint=http://localhost:4317 \
-jar target/*.jar
  • Replace <path/to> with the collector's directory.
  • Replace <YOUR-SERVICE-NAME> with the tracing service name.

Control the number of spans

Use the sampling option in the Java agent to limit outgoing spans.

The sampler configures whether spans will be recorded for any call to SpanBuilder.startSpan.

System propertyEnvironment variableDescription
otel.traces.samplerOTEL_TRACES_SAMPLERThe sampler to use for tracing. Defaults to parentbased_always_on
otel.traces.sampler.argOTEL_TRACES_SAMPLER_ARGAn argument to the configured tracer if supported, for example a ratio.

Supported values for otel.traces.sampler are

  • "always_on": AlwaysOnSampler
  • "always_off": AlwaysOffSampler
  • "traceidratio": TraceIdRatioBased. otel.traces.sampler.arg sets the ratio.
  • "parentbased_always_on": ParentBased(root=AlwaysOnSampler)
  • "parentbased_always_off": ParentBased(root=AlwaysOffSampler)
  • "parentbased_traceidratio": ParentBased(root=TraceIdRatioBased). otel.traces.sampler.arg sets the ratio.

Viewing Traces in Logz.io

Give your traces time to process, after which they'll be available in your Tracing dashboard.