mirror of
https://github.com/tiennm99/tjahzi.git
synced 2026-10-15 12:15:11 +00:00
Merge pull request #88 from tkowalcz/87-https-url-config
87 https url config
This commit is contained in:
34 files changed
+1141
-41
No files matched your search
@@ -15,8 +15,6 @@ import javax.net.ssl.SSLException;
|
||||
|
||||
public class BootstrapUtil {
|
||||
|
||||
public static final int HTTPS_PORT = 443;
|
||||
|
||||
public static ChannelFuture initConnection(
|
||||
EventLoopGroup group,
|
||||
ClientConfiguration clientConfiguration,
|
||||
@@ -25,7 +23,7 @@ public class BootstrapUtil {
|
||||
Bootstrap bootstrap = new Bootstrap();
|
||||
|
||||
SslContext sslContext = null;
|
||||
if (isSsl(clientConfiguration)) {
|
||||
if (clientConfiguration.isUseSSL()) {
|
||||
sslContext = createSslContext();
|
||||
}
|
||||
|
||||
@@ -49,10 +47,6 @@ public class BootstrapUtil {
|
||||
).connect();
|
||||
}
|
||||
|
||||
private static boolean isSsl(ClientConfiguration clientConfiguration) {
|
||||
return clientConfiguration.getPort() == HTTPS_PORT;
|
||||
}
|
||||
|
||||
private static SslContext createSslContext() {
|
||||
try {
|
||||
return SslContextBuilder.forClient()
|
||||
|
||||
@@ -5,6 +5,7 @@ public class ClientConfiguration {
|
||||
private final String logEndpoint;
|
||||
private final String host;
|
||||
private final int port;
|
||||
private final boolean useSSL;
|
||||
|
||||
private final String username;
|
||||
private final String password;
|
||||
@@ -19,6 +20,7 @@ public class ClientConfiguration {
|
||||
String logEndpoint,
|
||||
String host,
|
||||
int port,
|
||||
boolean useSSL,
|
||||
String username,
|
||||
String password,
|
||||
int connectionTimeoutMillis,
|
||||
@@ -28,6 +30,7 @@ public class ClientConfiguration {
|
||||
this.logEndpoint = logEndpoint;
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
this.useSSL = useSSL;
|
||||
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
@@ -51,6 +54,10 @@ public class ClientConfiguration {
|
||||
return port;
|
||||
}
|
||||
|
||||
public boolean isUseSSL() {
|
||||
return useSSL;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
package pl.tkowalcz.tjahzi.http;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
|
||||
public class ClientConfigurationBuilder {
|
||||
|
||||
public static final int HTTPS_PORT = 443;
|
||||
public static final int HTTP_PORT = 80;
|
||||
public static final String HTTPS_STRING = "https";
|
||||
public static final String HTTP_STRING = "http";
|
||||
|
||||
public static final String DEFAULT_LOG_ENDPOINT = "/loki/api/v1/push";
|
||||
|
||||
public static final int DEFAULT_CONNECT_TIMEOUT_MILLIS = 5_000;
|
||||
@@ -11,10 +19,15 @@ public class ClientConfigurationBuilder {
|
||||
|
||||
public static final int DEFAULT_MAX_RETRIES = 0;
|
||||
|
||||
private String logEndpoint = DEFAULT_LOG_ENDPOINT;
|
||||
private String logEndpoint;
|
||||
|
||||
private String url;
|
||||
|
||||
private String host;
|
||||
private int port;
|
||||
|
||||
private boolean useSSL;
|
||||
|
||||
private String username;
|
||||
private String password;
|
||||
|
||||
@@ -39,6 +52,11 @@ public class ClientConfigurationBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClientConfigurationBuilder withUseSSL(boolean useSSL) {
|
||||
this.useSSL = useSSL;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClientConfigurationBuilder withUsername(String username) {
|
||||
this.username = username;
|
||||
return this;
|
||||
@@ -69,15 +87,29 @@ public class ClientConfigurationBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClientConfigurationBuilder withUrl(String url) {
|
||||
this.url = url;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClientConfiguration build() {
|
||||
if (maxRequestsInFlight <= 0) {
|
||||
throw new IllegalArgumentException("Property maxRequestsInFlight must be greater than 0");
|
||||
}
|
||||
|
||||
return new ClientConfiguration(
|
||||
logEndpoint,
|
||||
ConnectionParams connectionParams = ConnectionParamsFactory.create(
|
||||
url,
|
||||
host,
|
||||
port,
|
||||
logEndpoint,
|
||||
useSSL
|
||||
);
|
||||
|
||||
return new ClientConfiguration(
|
||||
connectionParams.getLogEndpoint(),
|
||||
connectionParams.getHost(),
|
||||
connectionParams.getPort(),
|
||||
connectionParams.isUseSSL(),
|
||||
username,
|
||||
password,
|
||||
connectionTimeoutMillis,
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package pl.tkowalcz.tjahzi.http;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
|
||||
public class ConnectionParams {
|
||||
|
||||
private final String host;
|
||||
private final int port;
|
||||
|
||||
private final String logEndpoint;
|
||||
private final boolean useSSL;
|
||||
|
||||
public ConnectionParams(String host, int port, String logEndpoint, boolean useSSL) {
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
this.logEndpoint = logEndpoint;
|
||||
this.useSSL = useSSL;
|
||||
}
|
||||
|
||||
public String getHost() {
|
||||
return host;
|
||||
}
|
||||
|
||||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
public String getLogEndpoint() {
|
||||
return logEndpoint;
|
||||
}
|
||||
|
||||
public boolean isUseSSL() {
|
||||
return useSSL;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
package pl.tkowalcz.tjahzi.http;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
|
||||
public class ConnectionParamsFactory {
|
||||
|
||||
public static final int HTTPS_PORT = 443;
|
||||
public static final int HTTP_PORT = 80;
|
||||
public static final String HTTPS_STRING = "https";
|
||||
public static final String HTTP_STRING = "http";
|
||||
|
||||
public static final String DEFAULT_LOG_ENDPOINT = "/loki/api/v1/push";
|
||||
|
||||
public static ConnectionParams create(String url, String host, int port, String logEndpoint, boolean useSSL) {
|
||||
if (url != null && host != null) {
|
||||
throw new IllegalArgumentException("Only one of 'url' or 'host' can be configured. " +
|
||||
"Current configuration sets url to '" + url + "' and host to '" + host + "'");
|
||||
}
|
||||
|
||||
if (url != null && port != 0) {
|
||||
throw new IllegalArgumentException("Only one of 'url' or 'port' can be configured. " +
|
||||
"If using url you must configure port as part of url string. Current configuration sets url to '" + url + "' and port to '" + port + "'");
|
||||
}
|
||||
|
||||
if (url != null && useSSL) {
|
||||
throw new IllegalArgumentException("Only one of 'url' or 'useSSL' can be configured. " +
|
||||
"Please use 'https' prefix in url to enable SSL. Current configuration sets url to '" + url + "' and useSSL to '" + useSSL + "'");
|
||||
}
|
||||
|
||||
if (url == null && host == null) {
|
||||
throw new IllegalArgumentException("One of 'url' or 'host' must be configured.");
|
||||
}
|
||||
|
||||
if (host != null) {
|
||||
return validateAndConfigureConnectionParametersNoUrl(host, port, logEndpoint, useSSL);
|
||||
}
|
||||
|
||||
return validateAndConfigureConnectionParametersWithUrl(url, logEndpoint, useSSL);
|
||||
}
|
||||
|
||||
private static ConnectionParams validateAndConfigureConnectionParametersNoUrl(String host, int port, String logEndpoint, boolean useSSL) {
|
||||
if (logEndpoint == null) {
|
||||
logEndpoint = DEFAULT_LOG_ENDPOINT;
|
||||
}
|
||||
|
||||
if (!useSSL) {
|
||||
useSSL = port == HTTPS_PORT;
|
||||
}
|
||||
|
||||
return new ConnectionParams(host, port, logEndpoint, useSSL);
|
||||
}
|
||||
|
||||
private static ConnectionParams validateAndConfigureConnectionParametersWithUrl(String url, String logEndpoint, boolean useSSL) {
|
||||
try {
|
||||
URL parsedUrl = new URL(url);
|
||||
String host = parsedUrl.getHost();
|
||||
int port = configurePort(parsedUrl);
|
||||
|
||||
useSSL = validateProtocolSchemeAndConfigureSSL(parsedUrl);
|
||||
logEndpoint = configureLogEndpoint(parsedUrl, logEndpoint);
|
||||
|
||||
return new ConnectionParams(host, port, logEndpoint, useSSL);
|
||||
} catch (MalformedURLException e) {
|
||||
throw new IllegalArgumentException("Creating configuration with Loki URL failed, url: '" + url + "'", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static String configureLogEndpoint(URL parsedUrl, String logEndpoint) {
|
||||
if (logEndpoint != null) {
|
||||
throw new IllegalArgumentException("Only one of 'url' or 'logEndpoint' can be configured. " +
|
||||
"Embed log endpoint path into url. " +
|
||||
"Log endpoint: '" + logEndpoint + "', url: '" + parsedUrl + "'");
|
||||
}
|
||||
|
||||
if (parsedUrl.getPath() != null && !parsedUrl.getPath().isEmpty()) {
|
||||
return parsedUrl.getPath();
|
||||
} else {
|
||||
return DEFAULT_LOG_ENDPOINT;
|
||||
}
|
||||
}
|
||||
|
||||
private static int configurePort(URL parsedUrl) {
|
||||
int port = parsedUrl.getPort();
|
||||
|
||||
if (port == -1) {
|
||||
if (HTTPS_STRING.equalsIgnoreCase(parsedUrl.getProtocol())) {
|
||||
return HTTPS_PORT;
|
||||
} else {
|
||||
return HTTP_PORT;
|
||||
}
|
||||
}
|
||||
|
||||
return port;
|
||||
}
|
||||
|
||||
private static boolean validateProtocolSchemeAndConfigureSSL(URL parsedUrl) {
|
||||
String protocolScheme = parsedUrl.getProtocol();
|
||||
|
||||
if (HTTPS_STRING.equalsIgnoreCase(protocolScheme)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (HTTP_STRING.equalsIgnoreCase(protocolScheme)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Unknown protocol scheme, must be one of 'https' or 'http' (case insensitive). Provided: '" + protocolScheme + "'");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
package pl.tkowalcz.tjahzi.http;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class ClientConfigurationBuilderTest {
|
||||
|
||||
static Stream<Arguments> validConfigOptions() {
|
||||
return Stream.of(
|
||||
Arguments.of(
|
||||
"Should take arguments from url",
|
||||
"https://example.net:8443/monitoring/loki/api/v1/push",
|
||||
"example.net",
|
||||
8443,
|
||||
"/monitoring/loki/api/v1/push",
|
||||
true
|
||||
),
|
||||
Arguments.of(
|
||||
"Should not enable ssl if prefix is not https",
|
||||
"http://example.net:8443/monitoring/loki/api/v1/push",
|
||||
"example.net",
|
||||
8443,
|
||||
"/monitoring/loki/api/v1/push",
|
||||
false
|
||||
),
|
||||
Arguments.of(
|
||||
"Should use default logEndpoint if none is provided",
|
||||
"http://example.net:8443",
|
||||
"example.net",
|
||||
8443,
|
||||
"/loki/api/v1/push",
|
||||
false
|
||||
),
|
||||
Arguments.of(
|
||||
"Should use default http port if none is provided",
|
||||
"http://example.net",
|
||||
"example.net",
|
||||
80,
|
||||
"/loki/api/v1/push",
|
||||
false
|
||||
),
|
||||
Arguments.of(
|
||||
"Should use default https port if none is provided",
|
||||
"https://example.net",
|
||||
"example.net",
|
||||
443,
|
||||
"/loki/api/v1/push",
|
||||
true
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
static Stream<Arguments> invalidConfigOptions() {
|
||||
return Stream.of(
|
||||
Arguments.of(
|
||||
"Should reject unknown protocol",
|
||||
ClientConfiguration.builder().withUrl("file://example.net"),
|
||||
"Unknown protocol scheme, must be one of 'https' or 'http' (case insensitive). Provided: 'file'"
|
||||
),
|
||||
Arguments.of(
|
||||
"Should not allow url and host",
|
||||
ClientConfiguration.builder().withUrl("file://example.net").withHost("blah"),
|
||||
"Only one of 'url' or 'host' can be configured. Current configuration sets url to 'file://example.net' and host to 'blah'"
|
||||
),
|
||||
Arguments.of(
|
||||
"Should not allow url and port",
|
||||
ClientConfiguration.builder().withUrl("file://example.net").withPort(10),
|
||||
"Only one of 'url' or 'port' can be configured. If using url you must configure port as part of url string. Current configuration sets url to 'file://example.net' and port to '10'"
|
||||
),
|
||||
Arguments.of(
|
||||
"Should not allow url and useSSL to be defined",
|
||||
ClientConfiguration.builder().withUrl("file://example.net").withUseSSL(true),
|
||||
"Only one of 'url' or 'useSSL' can be configured. Please use 'https' prefix in url to enable SSL. Current configuration sets url to 'file://example.net' and useSSL to 'true'"
|
||||
),
|
||||
Arguments.of(
|
||||
"Should require max requests in flight to be positive",
|
||||
ClientConfiguration.builder().withHost("example.net").withMaxRequestsInFlight(-10),
|
||||
"Property maxRequestsInFlight must be greater than 0"
|
||||
),
|
||||
Arguments.of(
|
||||
"Should require at least url or host to be specified",
|
||||
ClientConfiguration.builder(),
|
||||
"One of 'url' or 'host' must be configured."
|
||||
),
|
||||
Arguments.of(
|
||||
"Should not allow url and logEndpoint to be specified at the same time",
|
||||
ClientConfiguration.builder().withUrl("http://example.net").withLogEndpoint("blah") ,
|
||||
"Only one of 'url' or 'logEndpoint' can be configured. Embed log endpoint path into url. Log endpoint: 'blah', url: 'http://example.net'"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource("validConfigOptions")
|
||||
void shouldCorrectlyInterpretUrl(
|
||||
String testCase,
|
||||
String url,
|
||||
String expectedHost,
|
||||
int expectedPort,
|
||||
String expectedLogEndpoint,
|
||||
boolean expectedUseSSL
|
||||
) {
|
||||
// Given
|
||||
ClientConfigurationBuilder configurationBuilder = new ClientConfigurationBuilder();
|
||||
|
||||
// When
|
||||
ClientConfiguration configuration = configurationBuilder
|
||||
.withUrl(url)
|
||||
.build();
|
||||
|
||||
// Then
|
||||
assertThat(configuration.getHost()).isEqualTo(expectedHost);
|
||||
assertThat(configuration.getPort()).isEqualTo(expectedPort);
|
||||
assertThat(configuration.getLogEndpoint()).isEqualTo(expectedLogEndpoint);
|
||||
assertThat(configuration.isUseSSL()).isEqualTo(expectedUseSSL);
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource("invalidConfigOptions")
|
||||
void shouldRejectUnknownProtocolScheme(
|
||||
String testCase,
|
||||
ClientConfigurationBuilder clientConfigurationBuilder,
|
||||
String expectedExceptionMessage
|
||||
) {
|
||||
// When
|
||||
IllegalArgumentException exception = Assertions.assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
clientConfigurationBuilder::build
|
||||
);
|
||||
|
||||
// Then
|
||||
assertThat(exception).hasMessage(expectedExceptionMessage);
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ class HttpHeadersFactoryTest {
|
||||
void shouldAddAuthHeaderIfCredentialsAreProvided_NoAdditionalHeaders() {
|
||||
// Given
|
||||
ClientConfiguration configuration = ClientConfiguration.builder()
|
||||
.withHost("blah")
|
||||
.withUsername("foob")
|
||||
.withPassword("ar")
|
||||
.build();
|
||||
@@ -36,6 +37,7 @@ class HttpHeadersFactoryTest {
|
||||
void shouldAddAuthHeaderIfCredentialsAreProvided_WithAdditionalHeaders() {
|
||||
// Given
|
||||
ClientConfiguration configuration = ClientConfiguration.builder()
|
||||
.withHost("blah")
|
||||
.withUsername("foob")
|
||||
.withPassword("ar")
|
||||
.build();
|
||||
@@ -65,6 +67,7 @@ class HttpHeadersFactoryTest {
|
||||
void shouldNotAddAuthHeader_NoAdditionalHeaders() {
|
||||
// Given
|
||||
ClientConfiguration configuration = ClientConfiguration.builder()
|
||||
.withHost("blah")
|
||||
.build();
|
||||
|
||||
String[] additionalHeaders = new String[0];
|
||||
@@ -84,6 +87,7 @@ class HttpHeadersFactoryTest {
|
||||
void shouldNOtAddAuthHeader_WithAdditionalHeaders() {
|
||||
// Given
|
||||
ClientConfiguration configuration = ClientConfiguration.builder()
|
||||
.withHost("blah")
|
||||
.build();
|
||||
|
||||
String[] additionalHeaders = new String[]{
|
||||
|
||||
@@ -54,7 +54,7 @@ Log4j2 appender seemed like a good first.
|
||||
|
||||
### Note on Loki HTTP endpoint and host/port configuration
|
||||
|
||||
Tjahzi sends `POST` requests to `/loki/api/v1/push` HTTP endpoint. Specifying
|
||||
Tjahzi by default sends `POST` requests to `/loki/api/v1/push` HTTP endpoint. Specifying
|
||||
e.g. `<host>loki.mydomain.com</host><port>3100</port>`
|
||||
will configure the appender to call to URL: `http://loki.mydomain.com:3100/loki/api/v1/push`.
|
||||
|
||||
@@ -114,6 +114,58 @@ attribute of configuration so that the appender can be found.
|
||||
</configuration>
|
||||
```
|
||||
|
||||
### Configuring connection parameters individually and using URL
|
||||
|
||||
At a minimum Tjahzi needs host and port configuration to connect to Loki:
|
||||
|
||||
```xml
|
||||
<host>example.com</host>
|
||||
<port>3100</port>
|
||||
```
|
||||
|
||||
If port is equal to `443` then SSL will be used. You can also configure SSL manually:
|
||||
|
||||
```xml
|
||||
<host>example.com</host>
|
||||
<port>3100</port>
|
||||
|
||||
<useSSL>true</useSSL>
|
||||
```
|
||||
|
||||
You can also override the default endpoint to which Tjahzi sends data. This can be useful if Loki is behind reverse proxy and
|
||||
additional path mapping is used:
|
||||
|
||||
```xml
|
||||
<host>example.com</host>
|
||||
<port>3100</port>
|
||||
|
||||
<logEndpoint>/monitoring/loki/api/v1/push</logEndpoint>
|
||||
```
|
||||
|
||||
All these parameters can be configured in one place using URL:
|
||||
|
||||
```xml
|
||||
<url>https://example.com:56654/monitoring/loki/api/v1/push</url>
|
||||
```
|
||||
|
||||
Note that all other tags (host, port, useSSL, logEndpoint) cannot be used when using URL.
|
||||
|
||||
| Section | Default | Comment |
|
||||
|----------|----------------------------|-------------------------------------------------------------------------------------|
|
||||
| Protocol | None (must be provided) | Supported protocols are `http` and `https`. Https is equvalent to setting `useUSSL` |
|
||||
| Host | None (must be provided) | |
|
||||
| Port | 80 for http, 443 for https | You can use any port and SSL will still be used if protocol is set to https |
|
||||
| Path | '/loki/api/v1/push' | |
|
||||
|
||||
Some examples of correct URLs:
|
||||
|
||||
```xml
|
||||
<url>http://example.com</url>
|
||||
<url>https://example.com:56654</url>
|
||||
<url>http://example.com/monitoring/loki/api/v1/push</url>
|
||||
<url>https://example.com:3100/monitoring/foo/bar</url>
|
||||
```
|
||||
|
||||
### Lookups / variable substitution
|
||||
|
||||
Contents of the properties
|
||||
@@ -190,21 +242,34 @@ appender.loki.labels[1].value=log4j
|
||||
Let's go through the example config used in previous sections and analyze configuration options (**Note: Tags are
|
||||
case-insensitive**).
|
||||
|
||||
#### Host (required)
|
||||
#### Host (required unless URL is specified)
|
||||
|
||||
Network host address of Loki instance. Either IP address or host name. It will be passed to Netty and end up being
|
||||
resolved by call to `InetSocketAddress.createUnresolved`.
|
||||
|
||||
#### Port (required)
|
||||
#### Port (required unless URL is specified)
|
||||
|
||||
Port used for connecting to running Loki. Tjahzi by default uses plain HTTP but if the port is `443` then it will
|
||||
automatically switch to HTTPS.
|
||||
|
||||
#### Username
|
||||
#### useSSL (optional)
|
||||
|
||||
Enable secure (HTTPS) communication regardless of configured port number.
|
||||
|
||||
#### logEndpoint (optional)
|
||||
|
||||
Overrides the default endpoint to which Tjahzi sends data. This can be useful if Loki is behind reverse proxy and
|
||||
additional path mapping is used.
|
||||
|
||||
#### URL (optional - replaces usage of host, port, useSSL, logEndpoint)
|
||||
|
||||
Configure connection in one place instead of using host, port etc. See [this section](#configuring-connection-parameters-individually-and-using-URL).
|
||||
|
||||
#### Username (optional)
|
||||
|
||||
Username for HTTP basic auth.
|
||||
|
||||
#### Password
|
||||
#### Password (optional)
|
||||
|
||||
Password for HTTP basic auth.
|
||||
|
||||
|
||||
@@ -102,6 +102,12 @@
|
||||
<version>1.14.3</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>nginx</artifactId>
|
||||
<version>1.14.3</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<profiles>
|
||||
|
||||
@@ -4,7 +4,6 @@ import org.apache.logging.log4j.Logger;
|
||||
import org.apache.logging.log4j.core.appender.AbstractAppender;
|
||||
import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
|
||||
import org.apache.logging.log4j.core.config.plugins.PluginElement;
|
||||
import org.apache.logging.log4j.core.config.plugins.validation.constraints.Required;
|
||||
import org.apache.logging.log4j.status.StatusLogger;
|
||||
import pl.tkowalcz.tjahzi.LoggingSystem;
|
||||
import pl.tkowalcz.tjahzi.TjahziInitializer;
|
||||
@@ -37,13 +36,20 @@ public class LokiAppenderBuilder<B extends LokiAppenderBuilder<B>> extends Abstr
|
||||
private static final int BYTES_IN_KILOBYTE = 1024;
|
||||
|
||||
@PluginBuilderAttribute
|
||||
@Required(message = "No Loki address provided for LokiAppender")
|
||||
private String url;
|
||||
|
||||
@PluginBuilderAttribute
|
||||
private String logEndpoint;
|
||||
|
||||
@PluginBuilderAttribute
|
||||
private String host;
|
||||
|
||||
@PluginBuilderAttribute
|
||||
@Required(message = "No Loki port provided for LokiAppender")
|
||||
private int port;
|
||||
|
||||
@PluginBuilderAttribute
|
||||
private boolean useSSL;
|
||||
|
||||
@PluginBuilderAttribute
|
||||
private String username;
|
||||
|
||||
@@ -95,8 +101,11 @@ public class LokiAppenderBuilder<B extends LokiAppenderBuilder<B>> extends Abstr
|
||||
@Override
|
||||
public LokiAppender build() {
|
||||
ClientConfiguration configurationBuilder = ClientConfiguration.builder()
|
||||
.withUrl(url)
|
||||
.withLogEndpoint(logEndpoint)
|
||||
.withHost(host)
|
||||
.withPort(port)
|
||||
.withUseSSL(useSSL)
|
||||
.withUsername(username)
|
||||
.withPassword(password)
|
||||
.withConnectionTimeoutMillis(connectTimeoutMillis)
|
||||
@@ -168,6 +177,46 @@ public class LokiAppenderBuilder<B extends LokiAppenderBuilder<B>> extends Abstr
|
||||
return host;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public String getLogEndpoint() {
|
||||
return logEndpoint;
|
||||
}
|
||||
|
||||
public void setLogEndpoint(String logEndpoint) {
|
||||
this.logEndpoint = logEndpoint;
|
||||
}
|
||||
|
||||
public boolean isUseSSL() {
|
||||
return useSSL;
|
||||
}
|
||||
|
||||
public void setUseSSL(boolean useSSL) {
|
||||
this.useSSL = useSSL;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public int getConnectTimeoutMillis() {
|
||||
return connectTimeoutMillis;
|
||||
}
|
||||
|
||||
+2
-4
@@ -3,15 +3,13 @@ package pl.tkowalcz.tjahzi.log4j2;
|
||||
import org.apache.logging.log4j.core.LoggerContext;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static pl.tkowalcz.tjahzi.log4j2.infra.IntegrationTest.loadConfig;
|
||||
|
||||
class BufferSizeConfigurationTest {
|
||||
|
||||
@Test
|
||||
void shouldUseDefaultWhenLogBufferSizeIsNotSetViaConfiguration() throws URISyntaxException {
|
||||
void shouldUseDefaultWhenLogBufferSizeIsNotSetViaConfiguration() {
|
||||
// When
|
||||
LoggerContext context = loadConfig("appender-test-with-buffer-size-megabytes-unset.xml");
|
||||
|
||||
@@ -22,7 +20,7 @@ class BufferSizeConfigurationTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSetLogBufferSizeViaConfiguration() throws URISyntaxException {
|
||||
void shouldSetLogBufferSizeViaConfiguration() {
|
||||
// When
|
||||
LoggerContext context = loadConfig("appender-test-with-buffer-size-megabytes-set-to-64.xml");
|
||||
|
||||
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
package pl.tkowalcz.tjahzi.log4j2;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.testcontainers.containers.BindMode;
|
||||
import org.testcontainers.containers.NginxContainer;
|
||||
import org.testcontainers.containers.wait.strategy.HttpWaitStrategy;
|
||||
import org.testcontainers.junit.jupiter.Container;
|
||||
import pl.tkowalcz.tjahzi.log4j2.infra.IntegrationTest;
|
||||
import pl.tkowalcz.tjahzi.log4j2.infra.LokiAssert;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
|
||||
class LokiAppenderHttpsCustomEndpointTest extends IntegrationTest {
|
||||
|
||||
@Container
|
||||
public NginxContainer<?> nginx = new NginxContainer<>("nginx:latest")
|
||||
.withNetwork(network)
|
||||
.withClasspathResourceMapping("loki.reverse.nginx.conf",
|
||||
"/etc/nginx/conf.d/loki.reverse.conf",
|
||||
BindMode.READ_ONLY
|
||||
)
|
||||
.withClasspathResourceMapping("nginx/nginx-selfsigned.crt",
|
||||
"/etc/nginx/nginx-selfsigned.crt",
|
||||
BindMode.READ_ONLY
|
||||
)
|
||||
.withClasspathResourceMapping("nginx/nginx-selfsigned.key",
|
||||
"/etc/nginx/nginx-selfsigned.key",
|
||||
BindMode.READ_ONLY
|
||||
)
|
||||
.withClasspathResourceMapping("nginx/passwords",
|
||||
"/etc/nginx/passwords",
|
||||
BindMode.READ_ONLY
|
||||
)
|
||||
.waitingFor(new HttpWaitStrategy())
|
||||
.withExposedPorts(81);
|
||||
|
||||
@Override
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
System.setProperty("loki.host", nginx.getHost());
|
||||
System.setProperty("loki.port", nginx.getFirstMappedPort().toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSendData() {
|
||||
// Given
|
||||
loadConfig("appender-test-custom-endpoint-configuration.xml");
|
||||
Logger logger = LogManager.getLogger(LokiAppenderHttpsCustomEndpointTest.class);
|
||||
|
||||
String expectedLogLine = "Hello World";
|
||||
|
||||
// When
|
||||
logger.info(expectedLogLine);
|
||||
|
||||
// Then
|
||||
LokiAssert.assertThat(loki)
|
||||
.returns(response -> response
|
||||
.body("data.result.size()", equalTo(1))
|
||||
.body("data.result[0].stream.server", equalTo("127.0.0.1"))
|
||||
.body(
|
||||
"data.result.values",
|
||||
hasItems(
|
||||
hasItems(
|
||||
hasItems(
|
||||
containsString("INFO LokiAppenderHttpsCustomEndpointTest - " + expectedLogLine)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
package pl.tkowalcz.tjahzi.log4j2;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.testcontainers.containers.BindMode;
|
||||
import org.testcontainers.containers.NginxContainer;
|
||||
import org.testcontainers.containers.wait.strategy.HttpWaitStrategy;
|
||||
import org.testcontainers.junit.jupiter.Container;
|
||||
import pl.tkowalcz.tjahzi.log4j2.infra.IntegrationTest;
|
||||
import pl.tkowalcz.tjahzi.log4j2.infra.LokiAssert;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
|
||||
class LokiAppenderHttpsUrlConnectionReverseProxyTest extends IntegrationTest {
|
||||
|
||||
@Container
|
||||
public NginxContainer<?> nginx = new NginxContainer<>("nginx:latest")
|
||||
.withNetwork(network)
|
||||
.withClasspathResourceMapping("loki.reverse.nginx.conf",
|
||||
"/etc/nginx/conf.d/loki.reverse.conf",
|
||||
BindMode.READ_ONLY
|
||||
)
|
||||
.withClasspathResourceMapping("nginx/nginx-selfsigned.crt",
|
||||
"/etc/nginx/nginx-selfsigned.crt",
|
||||
BindMode.READ_ONLY
|
||||
)
|
||||
.withClasspathResourceMapping("nginx/nginx-selfsigned.key",
|
||||
"/etc/nginx/nginx-selfsigned.key",
|
||||
BindMode.READ_ONLY
|
||||
)
|
||||
.withClasspathResourceMapping("nginx/passwords",
|
||||
"/etc/nginx/passwords",
|
||||
BindMode.READ_ONLY
|
||||
)
|
||||
.waitingFor(new HttpWaitStrategy())
|
||||
.withExposedPorts(81);
|
||||
|
||||
@Override
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
System.setProperty("loki.host", nginx.getHost());
|
||||
System.setProperty("loki.port", nginx.getFirstMappedPort().toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSendData() {
|
||||
// Given
|
||||
loadConfig("appender-test-url-configuration.xml");
|
||||
Logger logger = LogManager.getLogger(LokiAppenderHttpsUrlConnectionReverseProxyTest.class);
|
||||
|
||||
String expectedLogLine = "Hello World";
|
||||
|
||||
// When
|
||||
logger.info(expectedLogLine);
|
||||
|
||||
// Then
|
||||
LokiAssert.assertThat(loki)
|
||||
.returns(response -> response
|
||||
.body("data.result.size()", equalTo(1))
|
||||
.body("data.result[0].stream.server", equalTo("127.0.0.1"))
|
||||
.body(
|
||||
"data.result.values",
|
||||
hasItems(
|
||||
hasItems(
|
||||
hasItems(
|
||||
containsString("INFO LokiAppenderHttpsUrlConnectionReverseProxyTest - " + expectedLogLine)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.testcontainers.containers.BindMode;
|
||||
import org.testcontainers.containers.GenericContainer;
|
||||
import org.testcontainers.containers.Network;
|
||||
import org.testcontainers.containers.wait.strategy.Wait;
|
||||
import org.testcontainers.junit.jupiter.Container;
|
||||
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
@@ -17,8 +18,12 @@ import java.net.URL;
|
||||
@Testcontainers
|
||||
public class IntegrationTest {
|
||||
|
||||
public static Network network = Network.newNetwork();
|
||||
|
||||
@Container
|
||||
public GenericContainer loki = new GenericContainer("grafana/loki:2.3.0")
|
||||
.withNetwork(network)
|
||||
.withNetworkAliases("loki")
|
||||
.withCommand("-config.file=/etc/loki-config.yaml")
|
||||
.withClasspathResourceMapping("loki-config.yaml",
|
||||
"/etc/loki-config.yaml",
|
||||
@@ -31,7 +36,7 @@ public class IntegrationTest {
|
||||
.withExposedPorts(3100);
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
public void setUp() {
|
||||
System.setProperty("loki.host", loki.getHost());
|
||||
System.setProperty("loki.port", loki.getFirstMappedPort().toString());
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration packages="pl.tkowalcz.tjahzi.log4j2">
|
||||
<Loggers>
|
||||
<Root level="OFF">
|
||||
</Root>
|
||||
|
||||
<Logger name="pl.tkowalcz" level="ALL">
|
||||
<AppenderRef ref="Loki"/>
|
||||
</Logger>
|
||||
</Loggers>
|
||||
|
||||
<appenders>
|
||||
<Loki name="Loki">
|
||||
<host>${sys:loki.host}</host>
|
||||
<port>${sys:loki.port}</port>
|
||||
|
||||
<useSSL>true</useSSL>
|
||||
<logEndpoint>/monitoring/loki/api/v1/push</logEndpoint>
|
||||
|
||||
<username>tjahzi</username>
|
||||
<password>tjahzi</password>
|
||||
|
||||
|
||||
<ThresholdFilter level="ALL"/>
|
||||
<PatternLayout>
|
||||
<Pattern>[%t] %d{DEFAULT} %5p %c{1} - %m%n%exception{full}</Pattern>
|
||||
</PatternLayout>
|
||||
|
||||
<Header name="server" value="127.0.0.1"/>
|
||||
<Label name="server" value="127.0.0.1"/>
|
||||
</Loki>
|
||||
</appenders>
|
||||
</configuration>
|
||||
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration packages="pl.tkowalcz.tjahzi.log4j2">
|
||||
<Loggers>
|
||||
<Root level="OFF">
|
||||
</Root>
|
||||
|
||||
<Logger name="pl.tkowalcz" level="ALL">
|
||||
<AppenderRef ref="Loki"/>
|
||||
</Logger>
|
||||
</Loggers>
|
||||
|
||||
<appenders>
|
||||
<Loki name="Loki">
|
||||
<url>https://${sys:loki.host}:${sys:loki.port}/monitoring/loki/api/v1/push</url>
|
||||
|
||||
<username>tjahzi</username>
|
||||
<password>tjahzi</password>
|
||||
|
||||
<ThresholdFilter level="ALL"/>
|
||||
<PatternLayout>
|
||||
<Pattern>[%t] %d{DEFAULT} %5p %c{1} - %m%n%exception{full}</Pattern>
|
||||
</PatternLayout>
|
||||
|
||||
<Header name="server" value="127.0.0.1"/>
|
||||
<Label name="server" value="127.0.0.1"/>
|
||||
</Loki>
|
||||
</appenders>
|
||||
</configuration>
|
||||
@@ -0,0 +1,12 @@
|
||||
server {
|
||||
listen 81 ssl;
|
||||
ssl_certificate nginx-selfsigned.crt;
|
||||
ssl_certificate_key nginx-selfsigned.key;
|
||||
|
||||
location /monitoring/ {
|
||||
proxy_pass http://loki:3100/;
|
||||
}
|
||||
|
||||
auth_basic "loki auth";
|
||||
auth_basic_user_file /etc/nginx/passwords;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICljCCAX4CCQDDkzDNweN3/jANBgkqhkiG9w0BAQsFADANMQswCQYDVQQGEwJQ
|
||||
TDAeFw0yMjA1MDMyMjA0MzRaFw0yMzA1MDMyMjA0MzRaMA0xCzAJBgNVBAYTAlBM
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx6glTZOdvg9TbDu6fk/L
|
||||
pZkWFegPALIkCf+ckk+yDSpyg+kuD4XR96NdcxRrMfQBCJGfTBRnpEI4VyZD5aBf
|
||||
xRoqN6DwRrE8fN7r25C5wi3O7Xewz1NKmVnll+RnjZH84UuC3g+5RMVg7xDM8jrw
|
||||
pdQnw8EY2bBfT8G+1JLWdFBdQDByblMJv+w0LdisqbV0A/bTdLMD8a2Pw7lbciMC
|
||||
s0rrdlfcga0QsI7PsraOjVzjl29Cm09YNUjeiR4dD6ZNSAC73qdi1n9OPfrct648
|
||||
Q/kbR2jiwHzvnQUwj/UCKncs1Vxc84YDaCxiMQYyaHxvaukUFWzE3SLu/OKtpHcE
|
||||
7wIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQBBRVyWs2RLG3WJoqx7ArSXAg43uikv
|
||||
D7Tvo+ZIRuX7GRtQTk5N3nhyG5EVY85j9LX1MlaTkLeuAjHFuWjgRXe6cSFThnrA
|
||||
qZRMH19jIDlTsd1YOurGrNQCST6R6YJ3rrPrcRDuLow/nO+4hD903DzzuLSjqoWw
|
||||
TRIRwEcKuX/YrXX81/uFLEonuL97N4HkDnIhPZy/ndS8qAdrH6T47XZ6jh4WYInC
|
||||
q0FNEmIjbGgTqwOh5wARP83aR6azXpS8Wb6ms2LYBd1BDuoAkqX4Hw5/iHcsqBQ8
|
||||
UNp7qAH4UQj+cgUL0MQUL07NhJpbcAuAjz6iUXyFv/bb9WR2t2h2qLOu
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,28 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDHqCVNk52+D1Ns
|
||||
O7p+T8ulmRYV6A8AsiQJ/5yST7INKnKD6S4PhdH3o11zFGsx9AEIkZ9MFGekQjhX
|
||||
JkPloF/FGio3oPBGsTx83uvbkLnCLc7td7DPU0qZWeWX5GeNkfzhS4LeD7lExWDv
|
||||
EMzyOvCl1CfDwRjZsF9Pwb7UktZ0UF1AMHJuUwm/7DQt2KyptXQD9tN0swPxrY/D
|
||||
uVtyIwKzSut2V9yBrRCwjs+yto6NXOOXb0KbT1g1SN6JHh0Ppk1IALvep2LWf049
|
||||
+ty3rjxD+RtHaOLAfO+dBTCP9QIqdyzVXFzzhgNoLGIxBjJofG9q6RQVbMTdIu78
|
||||
4q2kdwTvAgMBAAECggEANRmdQjKeY2wEDeccxHdIfDRo0U60nsTCHP6kj60ud53w
|
||||
cvDTVUGMBEaolfC6ScDRfYvtR/itT9xky5jbufibP/qje8QL42FD6xoGtJCbNu+j
|
||||
9zg8CAfo8TwdefY/LEHFdc31ckXQSmAMVcisGUhftFrmcIOsrkjgMIAg1rxu2OQG
|
||||
gLhC5CCESLcGJdHnWnxuQCb81u51b5gftRgoCTRqOBiHf+uopn/cYjCOCCHIwh2I
|
||||
Dr8yVDcxUIeXzaTnUk3epR4AcYePI8A41KCqIlhQPDy6VbbZmDwHUROMD05ON88z
|
||||
/PsTUrhAvlAuYXDioxkDXCmU7/4ccUZtg+tVsQe3EQKBgQDmzjimW0RMLKvx5f9V
|
||||
PTgqrWuQssE1CuDqZAyqianyjkFzE4MteIXVzBh0JreDpKipyUcGu8QnlrS+LCsj
|
||||
W0DczdIq+WZmTFE8MmMxx5EFxEgXbyidV9P+VeBLgQ5kGYTAOfb6M8oHbSuo9mnQ
|
||||
6WZDWUwSJ5k5YFFXVjZZp4V1fQKBgQDdc3v1mr9sRwiy5AD641AgeCjmYCMm90j4
|
||||
QEZr25IgyueT7tA13O/Yi56ggiiQtkN0OjVCqtpF+piWIyEjXprUGf/0c/w16yew
|
||||
Cfv4j2+DGYqsNrPXU3yBWCOseFm221E/obmvvi8F9aSftTs83dMw9QSYgnnbkmVz
|
||||
5aPQrZT/2wKBgC3pxhEpxKtWk5naQ/HD7OFogCZXuOGBWX6nCnH+UqVisRhgQE8S
|
||||
U6VVJWQts4HnhnnIBW4MehfZ/fh6ttYguAV0mCNeW4VsFxjfeSYU6f94rVKkFTM/
|
||||
nndQw/6hHKjC/spyfXQGS2Ew0WeaJTCezW4GXICcCfoE9AloqAUrsvvNAoGBANV6
|
||||
4MLxvRtalw4tMqypubKzY2pCv2Bu/s8i10Be2U33jKtViy6BoE22uuKs1wI6UmiX
|
||||
PITaQ7U2USZbEzUPnF5f+hjkPvyThxzzYw5I6A0P8Fj4GwK3Hs1p9GntUz8tyfN3
|
||||
qpO62YX7OgokIckZZuBJM3b6QRLk3ByQAg3HaF4FAoGBANUMBR3oUC5Eb7Uzr2oy
|
||||
xY5mQ9YpN3ePzVUPc1tV8UCILksnJmobL4MFLfH0DWMQpysxBhjKwoYm1UxgYnWK
|
||||
FtJcYxAybkPvon+cCzOeVcZKQY5Kq5cjBGRKAA79fDQjiFR+mtwVY7JcaDfK8hff
|
||||
OW7UOrsm/EpNhyG4IYjH8cLd
|
||||
-----END PRIVATE KEY-----
|
||||
@@ -0,0 +1 @@
|
||||
tjahzi:$apr1$7WwaD0YV$Z3WwsmYhmHM9Vsp7euiO21
|
||||
+71
-14
@@ -111,6 +111,58 @@ class `pl.tkowalcz.tjahzi.logback.LokiAppender`.
|
||||
</configuration>
|
||||
```
|
||||
|
||||
### Configuring connection parameters individually and using URL
|
||||
|
||||
At a minimum Tjahzi needs host and port configuration to connect to Loki:
|
||||
|
||||
```xml
|
||||
<host>example.com</host>
|
||||
<port>3100</port>
|
||||
```
|
||||
|
||||
If port is equal to `443` then SSL will be used. You can also configure SSL manually:
|
||||
|
||||
```xml
|
||||
<host>example.com</host>
|
||||
<port>3100</port>
|
||||
|
||||
<useSSL>true</useSSL>
|
||||
```
|
||||
|
||||
You can also override the endpoint to which Tjahzi sends data. This can be usefull if Loki is behind reverse proxy and
|
||||
additional path mapping is used:
|
||||
|
||||
```xml
|
||||
<host>example.com</host>
|
||||
<port>3100</port>
|
||||
|
||||
<logEndpoint>/monitoring/loki/api/v1/push</logEndpoint>
|
||||
```
|
||||
|
||||
All these parameters can be configured in one place using URL:
|
||||
|
||||
```xml
|
||||
<url>https://example.com:56654/monitoring/loki/api/v1/push</url>
|
||||
```
|
||||
|
||||
Note that all other tags (host, port, useSSL, logEndpoint) cannot be used when using URL.
|
||||
|
||||
| Section | Default | Comment |
|
||||
|----------|----------------------------|-------------------------------------------------------------------------------------|
|
||||
| Protocol | None (must be provided) | Supported protocols are `http` and `https`. Https is equvalent to setting `useUSSL` |
|
||||
| Host | None (must be provided) | |
|
||||
| Port | 80 for http, 443 for https | You can use any port and SSL will still be used if protocol is set to https |
|
||||
| Path | '/loki/api/v1/push' | |
|
||||
|
||||
Some examples of correct URLs:
|
||||
|
||||
```xml
|
||||
<url>http://example.com</url>
|
||||
<url>https://example.com:56654</url>
|
||||
<url>http://example.com/monitoring/loki/api/v1/push</url>
|
||||
<url>https://example.com:3100/monitoring/foo/bar</url>
|
||||
```
|
||||
|
||||
### Lookups / variable substitution
|
||||
|
||||
Contents of the properties are automatically interpolated by Logback (
|
||||
@@ -170,6 +222,8 @@ Let's go through the example config above and analyze configuration options (**N
|
||||
| Port | Self-explanatory :) |
|
||||
| Encoder | You need to specify pattern for the layout. See section below |
|
||||
|
||||
Note: Instead of 'host' and 'port' you can specify URL. See [this section](#configuring-connection-parameters-individually-and-using-URL).
|
||||
|
||||
#### Pattern definition
|
||||
|
||||
You can define layout and pattern in a traditional Logback way:
|
||||
@@ -190,17 +244,20 @@ or use Tjahzi optimized efficient, low allocation encoder:
|
||||
|
||||
#### Optional configuration parameters
|
||||
|
||||
| Tag | Default value | Description |
|
||||
|-----|---------------|-------------|
|
||||
| Header | - | This tag can be used multiple times to specify additional headers that are passed to Loki instance. One example is to pass a `X-Scope-OrgID` header when [running Loki in multi-tenant mode](https://grafana.com/docs/loki/latest/operations/authentication/). |
|
||||
| Label | - | Specify additional labels attached to each log line sent via this appender instance. See also note about [label naming](https://github.com/tkowalcz/tjahzi/wiki/Label-naming). |
|
||||
| LogLevelLabel | - | If defined then log level label of configured name will be added to each line sent to Loki. It will contain Logback log level e.g. `INFO`, `WARN` etc. See also note about [label naming](https://github.com/tkowalcz/tjahzi/wiki/Label-naming). |
|
||||
| BufferSizeMegabytes | 32 MB | Size of the `log buffer`. Must be power of two between 1MB and 1GB. See [log buffer sizing](https://github.com/tkowalcz/tjahzi/wiki/Log-buffer-sizing) for more explanations. |
|
||||
| MaxRetries | 3 | Maximum number of retries to perform when delivering log message to Loki. Log buffer data is delivered in order, one batch after the other, so too much retries will block delivery of subsequent log batches (on the other hand if we need to retry many times then next batches will probably fail too). |
|
||||
| ConnectTimeoutMillis | 5s | This configures socket connect timeout when connecting to Loki. After unsuccessful connection attempt it will continue to retry indefinitely employing exponential backoff (initial backoff = 250ms, maximum backoff = 30s, multiplier = 3). |
|
||||
| ReadTimeoutMillis | 60s | Sets socket read timeout on Loki connection. |
|
||||
| UseOffHeapBuffer | true | Whether Tjahzi should allocate native buffer for `Log buffer` component. We can go into a rabbit hole of divagations what are the implications of this. Most important in our view is that having 10s or 100s of MB of space taken out of heap is not very friendly to garbage collector which might have to occasionally copy it around. |
|
||||
| BatchSize | 100 KB | Like in [promtail configuration](https://grafana.com/docs/loki/latest/clients/promtail/configuration/) `maximum batch size (in bytes) of logs to accumulate before sending the batch to Loki`.|
|
||||
| BatchWait | 5s | Like in [promtail configuration](https://grafana.com/docs/loki/latest/clients/promtail/configuration/) `maximum amount of time to wait before sending a batch, even if that batch isn't full`.|
|
||||
| logShipperWakeupIntervalMillis | 10 | The agent that reads data from log buffer, compresses it and sends to Loki via http is called `LogShipper`. This property controls how often it wakes up to perform its duties. Other properties control how often the data should be sent to Loki (`batchSize`, `batchWait`) this one just control how often to wake up and check for these conditions. In versions before `0.9.17` it was left at default 1ms which caused high CPU usage on some setups. |
|
||||
| shutdownTimeoutSeconds | 10s | On logging system shutdown (or config reload) Tjahzi will flush its internal buffers so that no logs are lost. This property sets limit on how long to wait for this to complete before proceeding with shutdown. |
|
||||
| Tag | Default value | Description |
|
||||
|--------------------------------|-----------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| Url | - | Configure connection in one place instead of using host, port etc. See [this section](#configuring-connection-parameters-individually-and-using-URL). |
|
||||
| UseSSL | true if port == '443' | Enable secure (HTTPS) communication regardless of configured port number. |
|
||||
| LogEndpoint | `/loki/api/v1/push` | Overrides the default endpoint to which Tjahzi sends data. This can be useful if Loki is behind reverse proxy and additional path mapping is used. |
|
||||
| Header | - | This tag can be used multiple times to specify additional headers that are passed to Loki instance. One example is to pass a `X-Scope-OrgID` header when [running Loki in multi-tenant mode](https://grafana.com/docs/loki/latest/operations/authentication/). |
|
||||
| Label | - | Specify additional labels attached to each log line sent via this appender instance. See also note about [label naming](https://github.com/tkowalcz/tjahzi/wiki/Label-naming). |
|
||||
| LogLevelLabel | - | If defined then log level label of configured name will be added to each line sent to Loki. It will contain Logback log level e.g. `INFO`, `WARN` etc. See also note about [label naming](https://github.com/tkowalcz/tjahzi/wiki/Label-naming). |
|
||||
| BufferSizeMegabytes | 32 MB | Size of the `log buffer`. Must be power of two between 1MB and 1GB. See [log buffer sizing](https://github.com/tkowalcz/tjahzi/wiki/Log-buffer-sizing) for more explanations. |
|
||||
| MaxRetries | 3 | Maximum number of retries to perform when delivering log message to Loki. Log buffer data is delivered in order, one batch after the other, so too much retries will block delivery of subsequent log batches (on the other hand if we need to retry many times then next batches will probably fail too). |
|
||||
| ConnectTimeoutMillis | 5s | This configures socket connect timeout when connecting to Loki. After unsuccessful connection attempt it will continue to retry indefinitely employing exponential backoff (initial backoff = 250ms, maximum backoff = 30s, multiplier = 3). |
|
||||
| ReadTimeoutMillis | 60s | Sets socket read timeout on Loki connection. |
|
||||
| UseOffHeapBuffer | true | Whether Tjahzi should allocate native buffer for `Log buffer` component. We can go into a rabbit hole of divagations what are the implications of this. Most important in our view is that having 10s or 100s of MB of space taken out of heap is not very friendly to garbage collector which might have to occasionally copy it around. |
|
||||
| BatchSize | 100 KB | Like in [promtail configuration](https://grafana.com/docs/loki/latest/clients/promtail/configuration/) `maximum batch size (in bytes) of logs to accumulate before sending the batch to Loki`. |
|
||||
| BatchWait | 5s | Like in [promtail configuration](https://grafana.com/docs/loki/latest/clients/promtail/configuration/) `maximum amount of time to wait before sending a batch, even if that batch isn't full`. |
|
||||
| logShipperWakeupIntervalMillis | 10 | The agent that reads data from log buffer, compresses it and sends to Loki via http is called `LogShipper`. This property controls how often it wakes up to perform its duties. Other properties control how often the data should be sent to Loki (`batchSize`, `batchWait`) this one just control how often to wake up and check for these conditions. In versions before `0.9.17` it was left at default 1ms which caused high CPU usage on some setups. |
|
||||
| shutdownTimeoutSeconds | 10s | On logging system shutdown (or config reload) Tjahzi will flush its internal buffers so that no logs are lost. This property sets limit on how long to wait for this to complete before proceeding with shutdown. |
|
||||
@@ -87,6 +87,12 @@
|
||||
<version>1.14.3</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>nginx</artifactId>
|
||||
<version>1.14.3</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
|
||||
+49
@@ -10,9 +10,18 @@ public abstract class LokiAppenderConfigurator extends UnsynchronizedAppenderBas
|
||||
|
||||
static final int BYTES_IN_MEGABYTE = 1024 * 1024;
|
||||
|
||||
private String url;
|
||||
private String logEndpoint;
|
||||
|
||||
private String host;
|
||||
private int port;
|
||||
|
||||
private boolean useSSL;
|
||||
|
||||
private String username;
|
||||
|
||||
private String password;
|
||||
|
||||
private int connectTimeoutMillis = 5000;
|
||||
private int readTimeoutMillis = 60_000;
|
||||
private int maxRetries = 3;
|
||||
@@ -33,6 +42,22 @@ public abstract class LokiAppenderConfigurator extends UnsynchronizedAppenderBas
|
||||
private final List<Header> headers = new ArrayList<>();
|
||||
private final List<Label> labels = new ArrayList<>();
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public String getLogEndpoint() {
|
||||
return logEndpoint;
|
||||
}
|
||||
|
||||
public void setLogEndpoint(String logEndpoint) {
|
||||
this.logEndpoint = logEndpoint;
|
||||
}
|
||||
|
||||
public String getHost() {
|
||||
return host;
|
||||
}
|
||||
@@ -49,6 +74,30 @@ public abstract class LokiAppenderConfigurator extends UnsynchronizedAppenderBas
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public boolean isUseSSL() {
|
||||
return useSSL;
|
||||
}
|
||||
|
||||
public void setUseSSL(boolean useSSL) {
|
||||
this.useSSL = useSSL;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public int getConnectTimeoutMillis() {
|
||||
return connectTimeoutMillis;
|
||||
}
|
||||
|
||||
@@ -40,9 +40,14 @@ public class LokiAppenderFactory {
|
||||
|
||||
public LoggingSystem createAppender() {
|
||||
ClientConfiguration configurationBuilder = ClientConfiguration.builder()
|
||||
.withUrl(configurator.getUrl())
|
||||
.withLogEndpoint(configurator.getLogEndpoint())
|
||||
.withHost(configurator.getHost())
|
||||
.withConnectionTimeoutMillis(configurator.getConnectTimeoutMillis())
|
||||
.withPort(configurator.getPort())
|
||||
.withUseSSL(configurator.isUseSSL())
|
||||
.withUsername(configurator.getUsername())
|
||||
.withPassword(configurator.getPassword())
|
||||
.withConnectionTimeoutMillis(configurator.getConnectTimeoutMillis())
|
||||
.withMaxRetries(configurator.getMaxRetries())
|
||||
.withRequestTimeoutMillis(configurator.getReadTimeoutMillis())
|
||||
.withMaxRequestsInFlight(configurator.getMaxRequestsInFlight())
|
||||
|
||||
+2
-2
@@ -10,7 +10,7 @@ import static pl.tkowalcz.tjahzi.logback.infra.IntegrationTest.loadConfig;
|
||||
class BufferSizeConfigurationTest {
|
||||
|
||||
@Test
|
||||
void shouldUseDefaultWhenLogBufferSizeIsNotSetViaConfiguration() throws Exception {
|
||||
void shouldUseDefaultWhenLogBufferSizeIsNotSetViaConfiguration() {
|
||||
// When
|
||||
LoggerContext context = loadConfig("appender-test-with-buffer-size-megabytes-unset.xml");
|
||||
|
||||
@@ -20,7 +20,7 @@ class BufferSizeConfigurationTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSetLogBufferSizeViaConfiguration() throws Exception {
|
||||
void shouldSetLogBufferSizeViaConfiguration() {
|
||||
// When
|
||||
LoggerContext context = loadConfig("appender-test-with-buffer-size-megabytes-set-to-64.xml");
|
||||
|
||||
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
package pl.tkowalcz.tjahzi.logback;
|
||||
|
||||
import ch.qos.logback.classic.LoggerContext;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.slf4j.Logger;
|
||||
import org.testcontainers.containers.BindMode;
|
||||
import org.testcontainers.containers.NginxContainer;
|
||||
import org.testcontainers.containers.wait.strategy.HttpWaitStrategy;
|
||||
import org.testcontainers.junit.jupiter.Container;
|
||||
import pl.tkowalcz.tjahzi.logback.infra.IntegrationTest;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static pl.tkowalcz.tjahzi.logback.infra.LokiAssert.assertThat;
|
||||
|
||||
class LokiAppenderHttpsCustomEndpointTest extends IntegrationTest {
|
||||
|
||||
@Container
|
||||
public NginxContainer<?> nginx = new NginxContainer<>("nginx:latest")
|
||||
.withNetwork(network)
|
||||
.withClasspathResourceMapping("loki.reverse.nginx.conf",
|
||||
"/etc/nginx/conf.d/loki.reverse.conf",
|
||||
BindMode.READ_ONLY
|
||||
)
|
||||
.withClasspathResourceMapping("nginx/nginx-selfsigned.crt",
|
||||
"/etc/nginx/nginx-selfsigned.crt",
|
||||
BindMode.READ_ONLY
|
||||
)
|
||||
.withClasspathResourceMapping("nginx/nginx-selfsigned.key",
|
||||
"/etc/nginx/nginx-selfsigned.key",
|
||||
BindMode.READ_ONLY
|
||||
)
|
||||
.withClasspathResourceMapping("nginx/passwords",
|
||||
"/etc/nginx/passwords",
|
||||
BindMode.READ_ONLY
|
||||
)
|
||||
.waitingFor(new HttpWaitStrategy())
|
||||
.withExposedPorts(81);
|
||||
|
||||
@Override
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
System.setProperty("loki.host", nginx.getHost());
|
||||
System.setProperty("loki.port", nginx.getFirstMappedPort().toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSendData() {
|
||||
// Given
|
||||
LoggerContext context = loadConfig("appender-test-custom-endpoint-configuration.xml");
|
||||
Logger logger = context.getLogger(LokiAppenderHttpsCustomEndpointTest.class);
|
||||
|
||||
String expectedLogLine = "Hello World";
|
||||
|
||||
// When
|
||||
logger.info(expectedLogLine);
|
||||
|
||||
// Then
|
||||
assertThat(loki)
|
||||
.returns(response -> response
|
||||
.body("data.result.size()", equalTo(1))
|
||||
.body("data.result[0].stream.server", equalTo("127.0.0.1"))
|
||||
.body(
|
||||
"data.result.values",
|
||||
hasItem(
|
||||
hasItems(
|
||||
hasItems(
|
||||
containsString("p.t.t.l.LokiAppenderHttpsCustomEndpointTest - " + expectedLogLine)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
package pl.tkowalcz.tjahzi.logback;
|
||||
|
||||
import ch.qos.logback.classic.LoggerContext;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.slf4j.Logger;
|
||||
import org.testcontainers.containers.BindMode;
|
||||
import org.testcontainers.containers.NginxContainer;
|
||||
import org.testcontainers.containers.wait.strategy.HttpWaitStrategy;
|
||||
import org.testcontainers.junit.jupiter.Container;
|
||||
import pl.tkowalcz.tjahzi.logback.infra.IntegrationTest;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static pl.tkowalcz.tjahzi.logback.infra.LokiAssert.assertThat;
|
||||
|
||||
class LokiAppenderHttpsUrlConnectionReverseProxyTest extends IntegrationTest {
|
||||
|
||||
@Container
|
||||
public NginxContainer<?> nginx = new NginxContainer<>("nginx:latest")
|
||||
.withNetwork(network)
|
||||
.withClasspathResourceMapping("loki.reverse.nginx.conf",
|
||||
"/etc/nginx/conf.d/loki.reverse.conf",
|
||||
BindMode.READ_ONLY
|
||||
)
|
||||
.withClasspathResourceMapping("nginx/nginx-selfsigned.crt",
|
||||
"/etc/nginx/nginx-selfsigned.crt",
|
||||
BindMode.READ_ONLY
|
||||
)
|
||||
.withClasspathResourceMapping("nginx/nginx-selfsigned.key",
|
||||
"/etc/nginx/nginx-selfsigned.key",
|
||||
BindMode.READ_ONLY
|
||||
)
|
||||
.withClasspathResourceMapping("nginx/passwords",
|
||||
"/etc/nginx/passwords",
|
||||
BindMode.READ_ONLY
|
||||
)
|
||||
.waitingFor(new HttpWaitStrategy())
|
||||
.withExposedPorts(81);
|
||||
|
||||
@Override
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
System.setProperty("loki.host", nginx.getHost());
|
||||
System.setProperty("loki.port", nginx.getFirstMappedPort().toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSendData() {
|
||||
// Given
|
||||
LoggerContext context = loadConfig("appender-test-url-configuration.xml");
|
||||
Logger logger = context.getLogger(LokiAppenderHttpsUrlConnectionReverseProxyTest.class);
|
||||
|
||||
String expectedLogLine = "Hello World";
|
||||
|
||||
// When
|
||||
logger.info(expectedLogLine);
|
||||
|
||||
// Then
|
||||
assertThat(loki)
|
||||
.returns(response -> response
|
||||
.body("data.result.size()", equalTo(1))
|
||||
.body("data.result[0].stream.server", equalTo("127.0.0.1"))
|
||||
.body(
|
||||
"data.result.values",
|
||||
hasItem(
|
||||
hasItems(
|
||||
hasItems(
|
||||
containsString("p.t.t.l.LokiAppenderHttpsUrlConnectionReverseProxyTest - " + expectedLogLine)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
+6
-1
@@ -8,6 +8,7 @@ import org.junit.jupiter.api.BeforeEach;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.testcontainers.containers.BindMode;
|
||||
import org.testcontainers.containers.GenericContainer;
|
||||
import org.testcontainers.containers.Network;
|
||||
import org.testcontainers.containers.wait.strategy.Wait;
|
||||
import org.testcontainers.junit.jupiter.Container;
|
||||
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
@@ -19,8 +20,12 @@ import java.net.URISyntaxException;
|
||||
@Testcontainers
|
||||
public class IntegrationTest {
|
||||
|
||||
public static Network network = Network.newNetwork();
|
||||
|
||||
@Container
|
||||
public GenericContainer loki = new GenericContainer("grafana/loki:2.3.0")
|
||||
.withNetwork(network)
|
||||
.withNetworkAliases("loki")
|
||||
.withCommand("-config.file=/etc/loki-config.yaml")
|
||||
.withClasspathResourceMapping("loki-config.yaml",
|
||||
"/etc/loki-config.yaml",
|
||||
@@ -33,7 +38,7 @@ public class IntegrationTest {
|
||||
.withExposedPorts(3100);
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
public void setUp() {
|
||||
System.setProperty("loki.host", loki.getHost());
|
||||
System.setProperty("loki.port", loki.getFirstMappedPort().toString());
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
<configuration debug="true">
|
||||
<appender name="Loki" class="pl.tkowalcz.tjahzi.logback.LokiAppender">
|
||||
<host>${loki.host}</host>
|
||||
<port>${loki.port}</port>
|
||||
|
||||
<useSSL>true</useSSL>
|
||||
<logEndpoint>/monitoring/loki/api/v1/push</logEndpoint>
|
||||
|
||||
<username>tjahzi</username>
|
||||
<password>tjahzi</password>
|
||||
|
||||
<efficientLayout>
|
||||
<pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
|
||||
</efficientLayout>
|
||||
|
||||
<header>
|
||||
<name>X-Org-Id</name>
|
||||
<value>Circus</value>
|
||||
</header>
|
||||
|
||||
<label>
|
||||
<name>server</name>
|
||||
<value>127.0.0.1</value>
|
||||
</label>
|
||||
</appender>
|
||||
|
||||
<root level="debug">
|
||||
<appender-ref ref="Loki"/>
|
||||
</root>
|
||||
</configuration>
|
||||
@@ -0,0 +1,26 @@
|
||||
<configuration debug="true">
|
||||
<appender name="Loki" class="pl.tkowalcz.tjahzi.logback.LokiAppender">
|
||||
<url>https://${loki.host}:${loki.port}/monitoring/loki/api/v1/push</url>
|
||||
|
||||
<username>tjahzi</username>
|
||||
<password>tjahzi</password>
|
||||
|
||||
<efficientLayout>
|
||||
<pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
|
||||
</efficientLayout>
|
||||
|
||||
<header>
|
||||
<name>X-Org-Id</name>
|
||||
<value>Circus</value>
|
||||
</header>
|
||||
|
||||
<label>
|
||||
<name>server</name>
|
||||
<value>127.0.0.1</value>
|
||||
</label>
|
||||
</appender>
|
||||
|
||||
<root level="debug">
|
||||
<appender-ref ref="Loki"/>
|
||||
</root>
|
||||
</configuration>
|
||||
@@ -0,0 +1,12 @@
|
||||
server {
|
||||
listen 81 ssl;
|
||||
ssl_certificate nginx-selfsigned.crt;
|
||||
ssl_certificate_key nginx-selfsigned.key;
|
||||
|
||||
location /monitoring/ {
|
||||
proxy_pass http://loki:3100/;
|
||||
}
|
||||
|
||||
auth_basic "loki auth";
|
||||
auth_basic_user_file /etc/nginx/passwords;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICljCCAX4CCQDDkzDNweN3/jANBgkqhkiG9w0BAQsFADANMQswCQYDVQQGEwJQ
|
||||
TDAeFw0yMjA1MDMyMjA0MzRaFw0yMzA1MDMyMjA0MzRaMA0xCzAJBgNVBAYTAlBM
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx6glTZOdvg9TbDu6fk/L
|
||||
pZkWFegPALIkCf+ckk+yDSpyg+kuD4XR96NdcxRrMfQBCJGfTBRnpEI4VyZD5aBf
|
||||
xRoqN6DwRrE8fN7r25C5wi3O7Xewz1NKmVnll+RnjZH84UuC3g+5RMVg7xDM8jrw
|
||||
pdQnw8EY2bBfT8G+1JLWdFBdQDByblMJv+w0LdisqbV0A/bTdLMD8a2Pw7lbciMC
|
||||
s0rrdlfcga0QsI7PsraOjVzjl29Cm09YNUjeiR4dD6ZNSAC73qdi1n9OPfrct648
|
||||
Q/kbR2jiwHzvnQUwj/UCKncs1Vxc84YDaCxiMQYyaHxvaukUFWzE3SLu/OKtpHcE
|
||||
7wIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQBBRVyWs2RLG3WJoqx7ArSXAg43uikv
|
||||
D7Tvo+ZIRuX7GRtQTk5N3nhyG5EVY85j9LX1MlaTkLeuAjHFuWjgRXe6cSFThnrA
|
||||
qZRMH19jIDlTsd1YOurGrNQCST6R6YJ3rrPrcRDuLow/nO+4hD903DzzuLSjqoWw
|
||||
TRIRwEcKuX/YrXX81/uFLEonuL97N4HkDnIhPZy/ndS8qAdrH6T47XZ6jh4WYInC
|
||||
q0FNEmIjbGgTqwOh5wARP83aR6azXpS8Wb6ms2LYBd1BDuoAkqX4Hw5/iHcsqBQ8
|
||||
UNp7qAH4UQj+cgUL0MQUL07NhJpbcAuAjz6iUXyFv/bb9WR2t2h2qLOu
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,28 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDHqCVNk52+D1Ns
|
||||
O7p+T8ulmRYV6A8AsiQJ/5yST7INKnKD6S4PhdH3o11zFGsx9AEIkZ9MFGekQjhX
|
||||
JkPloF/FGio3oPBGsTx83uvbkLnCLc7td7DPU0qZWeWX5GeNkfzhS4LeD7lExWDv
|
||||
EMzyOvCl1CfDwRjZsF9Pwb7UktZ0UF1AMHJuUwm/7DQt2KyptXQD9tN0swPxrY/D
|
||||
uVtyIwKzSut2V9yBrRCwjs+yto6NXOOXb0KbT1g1SN6JHh0Ppk1IALvep2LWf049
|
||||
+ty3rjxD+RtHaOLAfO+dBTCP9QIqdyzVXFzzhgNoLGIxBjJofG9q6RQVbMTdIu78
|
||||
4q2kdwTvAgMBAAECggEANRmdQjKeY2wEDeccxHdIfDRo0U60nsTCHP6kj60ud53w
|
||||
cvDTVUGMBEaolfC6ScDRfYvtR/itT9xky5jbufibP/qje8QL42FD6xoGtJCbNu+j
|
||||
9zg8CAfo8TwdefY/LEHFdc31ckXQSmAMVcisGUhftFrmcIOsrkjgMIAg1rxu2OQG
|
||||
gLhC5CCESLcGJdHnWnxuQCb81u51b5gftRgoCTRqOBiHf+uopn/cYjCOCCHIwh2I
|
||||
Dr8yVDcxUIeXzaTnUk3epR4AcYePI8A41KCqIlhQPDy6VbbZmDwHUROMD05ON88z
|
||||
/PsTUrhAvlAuYXDioxkDXCmU7/4ccUZtg+tVsQe3EQKBgQDmzjimW0RMLKvx5f9V
|
||||
PTgqrWuQssE1CuDqZAyqianyjkFzE4MteIXVzBh0JreDpKipyUcGu8QnlrS+LCsj
|
||||
W0DczdIq+WZmTFE8MmMxx5EFxEgXbyidV9P+VeBLgQ5kGYTAOfb6M8oHbSuo9mnQ
|
||||
6WZDWUwSJ5k5YFFXVjZZp4V1fQKBgQDdc3v1mr9sRwiy5AD641AgeCjmYCMm90j4
|
||||
QEZr25IgyueT7tA13O/Yi56ggiiQtkN0OjVCqtpF+piWIyEjXprUGf/0c/w16yew
|
||||
Cfv4j2+DGYqsNrPXU3yBWCOseFm221E/obmvvi8F9aSftTs83dMw9QSYgnnbkmVz
|
||||
5aPQrZT/2wKBgC3pxhEpxKtWk5naQ/HD7OFogCZXuOGBWX6nCnH+UqVisRhgQE8S
|
||||
U6VVJWQts4HnhnnIBW4MehfZ/fh6ttYguAV0mCNeW4VsFxjfeSYU6f94rVKkFTM/
|
||||
nndQw/6hHKjC/spyfXQGS2Ew0WeaJTCezW4GXICcCfoE9AloqAUrsvvNAoGBANV6
|
||||
4MLxvRtalw4tMqypubKzY2pCv2Bu/s8i10Be2U33jKtViy6BoE22uuKs1wI6UmiX
|
||||
PITaQ7U2USZbEzUPnF5f+hjkPvyThxzzYw5I6A0P8Fj4GwK3Hs1p9GntUz8tyfN3
|
||||
qpO62YX7OgokIckZZuBJM3b6QRLk3ByQAg3HaF4FAoGBANUMBR3oUC5Eb7Uzr2oy
|
||||
xY5mQ9YpN3ePzVUPc1tV8UCILksnJmobL4MFLfH0DWMQpysxBhjKwoYm1UxgYnWK
|
||||
FtJcYxAybkPvon+cCzOeVcZKQY5Kq5cjBGRKAA79fDQjiFR+mtwVY7JcaDfK8hff
|
||||
OW7UOrsm/EpNhyG4IYjH8cLd
|
||||
-----END PRIVATE KEY-----
|
||||
@@ -0,0 +1 @@
|
||||
tjahzi:$apr1$7WwaD0YV$Z3WwsmYhmHM9Vsp7euiO21
|
||||
Reference in new issue
Block a user