diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 0000000..f9abc7c
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,16 @@
+{
+ // Use IntelliSense to learn about possible attributes.
+ // Hover to view descriptions of existing attributes.
+ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "type": "java",
+ "name": "Run Manzan",
+ "request": "launch",
+ "mainClass": "com.github.theprez.manzan.ManzanMainApp",
+ "projectName": "manzan",
+ "args": "--configdir=${workspaceFolder}/config"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 0c0075f..7286ef3 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -103,5 +103,6 @@
"compare": "cpp",
"concepts": "cpp",
"algorithm": "cpp"
- }
+ },
+ "java.configuration.updateBuildConfiguration": "automatic"
}
\ No newline at end of file
diff --git a/camel/pom.xml b/camel/pom.xml
index 8ab90eb..2a8a7be 100644
--- a/camel/pom.xml
+++ b/camel/pom.xml
@@ -154,7 +154,11 @@
fluent-logger
0.3.4
-
+
+ io.github.mjfryc
+ mjaron-tinyloki-java
+ 0.3.11
+
diff --git a/camel/src/main/java/com/github/theprez/manzan/configuration/DestinationConfig.java b/camel/src/main/java/com/github/theprez/manzan/configuration/DestinationConfig.java
index 2433564..1302131 100644
--- a/camel/src/main/java/com/github/theprez/manzan/configuration/DestinationConfig.java
+++ b/camel/src/main/java/com/github/theprez/manzan/configuration/DestinationConfig.java
@@ -18,6 +18,7 @@
import com.github.theprez.manzan.routes.dest.EmailDestination;
import com.github.theprez.manzan.routes.dest.FileDestination;
import com.github.theprez.manzan.routes.dest.FluentDDestination;
+import com.github.theprez.manzan.routes.dest.GrafanaLokiDestination;
import com.github.theprez.manzan.routes.dest.HttpDestination;
import com.github.theprez.manzan.routes.dest.KafkaDestination;
import com.github.theprez.manzan.routes.dest.SentryDestination;
@@ -83,6 +84,12 @@ public synchronized Map getRoutes(CamelContext context) {
final String host = getRequiredString(name, "host");
final int port = getRequiredInt(name, "port");
ret.put(name, new FluentDDestination(name, tag, host, port));
+ }
+ case "loki": {
+ final String url = getRequiredString(name, "url");
+ final String username = getRequiredString(name, "username");
+ final String password = getRequiredString(name, "password");
+ ret.put(name, new GrafanaLokiDestination(name, url, username, password, format));
}
break;
case "smtp":
diff --git a/camel/src/main/java/com/github/theprez/manzan/routes/ManzanRoute.java b/camel/src/main/java/com/github/theprez/manzan/routes/ManzanRoute.java
index de3247d..f3c92cd 100644
--- a/camel/src/main/java/com/github/theprez/manzan/routes/ManzanRoute.java
+++ b/camel/src/main/java/com/github/theprez/manzan/routes/ManzanRoute.java
@@ -13,8 +13,23 @@ public abstract class ManzanRoute extends RouteBuilder {
protected static final String EVENT_TYPE = "event_type";
+ protected static final String SESSION_ID = "SESSION_ID";
+ protected static final String MSG_MESSAGE_ID = "MESSAGE_ID";
+ protected static final String MSG_MESSAGE_TYPE = "MESSAGE_TYPE";
+ protected static final String MSG_SEVERITY = "SEVERITY";
+ protected static final String JOB = "JOB";
+ protected static final String MSG_SENDING_USRPRF = "SENDING_USRPRF";
+ protected static final String MSG_SENDING_PROGRAM_NAME = "SENDING_PROGRAM_NAME";
+ protected static final String MSG_SENDING_MODULE_NAME = "SENDING_MODULE_NAME";
+ protected static final String MSG_SENDING_PROCEDURE_NAME = "SENDING_PROCEDURE_NAME";
+ protected static final String MSG_ORDINAL_POSITION = "ORDINAL_POSITION";
+ protected static final String MSG_MESSAGE_TIMESTAMP = "MESSAGE_TIMESTAMP";
+ protected static final String MSG_MESSAGE = "MESSAGE";
+
+ protected static final int SEVERITY_LIMIT = 29;
+
protected static String getWatchName(final Exchange exchange) {
- final Object watchNameObject = exchange.getIn().getHeader("session_id");
+ final Object watchNameObject = exchange.getIn().getHeader(SESSION_ID);
if (null == watchNameObject) {
throw new RuntimeException("Couldn't figure out watch ID");
}
@@ -45,6 +60,10 @@ protected Map getDataMap(final Exchange _exchange) {
return (Map) _exchange.getIn().getHeader("data_map");
}
+ protected T getBody(final Exchange _exchange, Class type) {
+ return _exchange.getIn().getBody(type);
+ }
+
protected String getInUri() {
return "direct:" + m_name;
}
diff --git a/camel/src/main/java/com/github/theprez/manzan/routes/dest/GrafanaLokiDestination.java b/camel/src/main/java/com/github/theprez/manzan/routes/dest/GrafanaLokiDestination.java
new file mode 100644
index 0000000..5a487f6
--- /dev/null
+++ b/camel/src/main/java/com/github/theprez/manzan/routes/dest/GrafanaLokiDestination.java
@@ -0,0 +1,82 @@
+package com.github.theprez.manzan.routes.dest;
+
+import java.sql.Timestamp;
+
+import com.github.theprez.manzan.ManzanEventType;
+import com.github.theprez.manzan.routes.ManzanRoute;
+
+import pl.mjaron.tinyloki.ILogStream;
+import pl.mjaron.tinyloki.Labels;
+import pl.mjaron.tinyloki.LogController;
+import pl.mjaron.tinyloki.StreamBuilder;
+import pl.mjaron.tinyloki.TinyLoki;
+
+public class GrafanaLokiDestination extends ManzanRoute {
+ private final LogController logController;
+ private final static String endpoint = "/loki/api/v1/push";
+ private final static String appLabelName = "app";
+ private final static String appLabelValue = "manzan";
+
+ public GrafanaLokiDestination(final String _name, final String _url, final String _username, final String _password,
+ final String _format) {
+ super(_name);
+
+ logController = TinyLoki
+ .withUrl(_url + endpoint)
+ .withBasicAuth(_username, _password)
+ .start();
+
+ Runtime.getRuntime().addShutdownHook(new Thread() {
+ @Override
+ public void run() {
+ try {
+ logController
+ .softStop()
+ .hardStop();
+ } catch (final Exception e) {
+ e.printStackTrace();
+ }
+ }
+ });
+ }
+
+ //@formatter:off
+ @Override
+ public void configure() {
+ from(getInUri())
+ .routeId(m_name).process(exchange -> {
+ final ManzanEventType type = (ManzanEventType) exchange.getIn().getHeader(EVENT_TYPE);
+ if(ManzanEventType.WATCH_MSG == type) {
+ StreamBuilder builder = logController
+ .stream()
+ .l(appLabelName, appLabelValue)
+ .l(Labels.LEVEL, ((Integer) get(exchange, MSG_SEVERITY)) > SEVERITY_LIMIT ? Labels.FATAL : Labels.INFO)
+ .l(SESSION_ID, getWatchName(exchange));
+
+ String[] keys = {
+ MSG_MESSAGE_ID,
+ MSG_MESSAGE_TYPE,
+ MSG_SEVERITY,
+ JOB,
+ MSG_SENDING_USRPRF,
+ MSG_SENDING_PROGRAM_NAME,
+ MSG_SENDING_MODULE_NAME,
+ MSG_SENDING_PROCEDURE_NAME
+ };
+
+ for (String key: keys) {
+ String value = getString(exchange, key);
+ if(!value.equals("")) {
+ builder.l(key, value);
+ }
+ }
+
+ ILogStream stream = builder.build();
+ stream.log(Timestamp.valueOf(getString(exchange, MSG_MESSAGE_TIMESTAMP)).getTime(), getBody(exchange, String.class));
+ } else {
+ throw new RuntimeException("Grafana Loki route doesn't know how to process type "+type);
+ }
+ });
+ }
+ //@formatter:on
+}
diff --git a/camel/src/main/java/com/github/theprez/manzan/routes/dest/SentryDestination.java b/camel/src/main/java/com/github/theprez/manzan/routes/dest/SentryDestination.java
index 71cee6a..5c697b9 100644
--- a/camel/src/main/java/com/github/theprez/manzan/routes/dest/SentryDestination.java
+++ b/camel/src/main/java/com/github/theprez/manzan/routes/dest/SentryDestination.java
@@ -16,16 +16,6 @@
public class SentryDestination extends ManzanRoute {
private static SentryDestination m_singleton = null;
- private static final String MSG_MESSAGE = "MESSAGE";
- private static final String MSG_MESSAGE_ID = "MESSAGE_ID";
- private static final String MSG_ORDINAL_POSITION = "ORDINAL_POSITION";
- private static final String MSG_SENDING_MODULE_NAME = "SENDING_MODULE_NAME";
-
- private static final String MSG_SENDING_PROCEDURE_NAME = "SENDING_PROCEDURE_NAME";
- private static final String MSG_SENDING_PROGRAM_NAME = "SENDING_PROGRAM_NAME";
- private static final String MSG_SENDING_USRPRF = "SENDING_USRPRF";
-
- private static final String MSG_SEVERITY = "SEVERITY";
public SentryDestination(final String _name, final String _dsn) {
super(_name);
@@ -68,7 +58,7 @@ public void configure() {
SentryLevel level;
final int sev = (Integer) get(exchange, MSG_SEVERITY);
- if (sev > 29) {
+ if (sev > SEVERITY_LIMIT) {
level = SentryLevel.ERROR;
} else {
level = SentryLevel.INFO;
diff --git a/camel/src/main/java/com/github/theprez/manzan/routes/dest/TwilioDestination.java b/camel/src/main/java/com/github/theprez/manzan/routes/dest/TwilioDestination.java
index cb57722..cdea30c 100644
--- a/camel/src/main/java/com/github/theprez/manzan/routes/dest/TwilioDestination.java
+++ b/camel/src/main/java/com/github/theprez/manzan/routes/dest/TwilioDestination.java
@@ -19,6 +19,6 @@ public TwilioDestination(CamelContext context, final String _name, final String
@Override
protected void customPostProcess(Exchange exchange) {
- exchange.getIn().setHeader("CamelTwilio.body", exchange.getIn().getBody(String.class));
+ exchange.getIn().setHeader("CamelTwilio.body", getBody(exchange, String.class));
}
}
diff --git a/camel/src/main/java/com/github/theprez/manzan/routes/event/FileEvent.java b/camel/src/main/java/com/github/theprez/manzan/routes/event/FileEvent.java
index 4dca12a..e2a6021 100644
--- a/camel/src/main/java/com/github/theprez/manzan/routes/event/FileEvent.java
+++ b/camel/src/main/java/com/github/theprez/manzan/routes/event/FileEvent.java
@@ -43,13 +43,13 @@ public void configure() {
.setHeader(EVENT_TYPE, constant(ManzanEventType.FILE))
.setBody(constant(m_file.getAbsolutePath()))
.process((exchange) -> {
- final File f = new File(exchange.getIn().getBody().toString());
+ final File f = new File(getBody(exchange, String.class));
exchange.getIn().setBody(new FileNewContentsReader(f, "*TAG"));
})
.split(body().tokenize("\n")).streaming().parallelProcessing(false).stopOnException()
.convertBodyTo(String.class)
.process(exchange -> {
- String bodyStr = exchange.getIn().getBody(String.class);
+ String bodyStr = getBody(exchange, String.class);
exchange.getIn().setHeader("abort", m_filter.matches(bodyStr) && StringUtils.isNonEmpty(bodyStr)?"continue":"abort");
})
.choice().when(simple("${header.abort} != 'abort'"))
@@ -57,7 +57,7 @@ public void configure() {
final Map data_map = new LinkedHashMap();
data_map.put(FILE_NAME, m_file.getName());
data_map.put(FILE_PATH, m_file.getAbsolutePath());
- data_map.put(FILE_DATA, exchange.getIn().getBody(String.class).replace("\r",""));
+ data_map.put(FILE_DATA, getBody(exchange, String.class).replace("\r",""));
exchange.getIn().setHeader("data_map", data_map);
exchange.getIn().setBody(data_map);
})
diff --git a/docs/README.md b/docs/README.md
index 35cb34c..a1d74ec 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -40,7 +40,7 @@ Many other destinations will be available. Examples include:
- [Google Drive](http://drive.google.com) ⏳
- [Google Mail (gmail)](http://gmail.com) ⏳
- [Google Pub/Sub](http://cloud.google.com/pubsub) ⏳
-- [Grafana Loki](https://grafana.com/oss/loki/) ⏳
+- [Grafana Loki](https://grafana.com/oss/loki/) ✅
- HTTP endpoints (REST, etc) ✅
- HTTPS endpoints (REST, etc) ✅
- [Internet of Things (mqtt)](https://www.eclipse.org/paho/) ⏳
diff --git a/docs/_sidebar.md b/docs/_sidebar.md
index d009f93..4e36f22 100644
--- a/docs/_sidebar.md
+++ b/docs/_sidebar.md
@@ -9,5 +9,6 @@
* Examples
* [File](config/examples/file.md)
* [Twilio](config/examples/twilio.md)
- * [Sentry watch](config/examples/sentry.md)
+ * [Sentry](config/examples/sentry.md)
+ * [Grafana Loki](config/examples/grafanaLoki.md)
* [Contributing](contributing.md)
\ No newline at end of file
diff --git a/docs/config/dests.md b/docs/config/dests.md
index 0fddb1c..fc46c56 100644
--- a/docs/config/dests.md
+++ b/docs/config/dests.md
@@ -20,15 +20,16 @@ As well, each section can provide `format` as an optional type.
Some types have additional properties that they require.
-| id | Description | Required properties | Optional properties |
-|------------------|---------------------------------|------------------------------------------------------------| |
-| `stdout` | Write all data to standard out. | None. | |
-| `slack` | Send data to a Slack channel | * `webhook`
* `channel`
| |
-| `fluentd` | Sent data to FluentD | * `tag`
* `host`
* `port`
| |
-| `smtp`/`smtps` | Sent data via email | * `server`
* `subject`
* `to`
* `from`
| * `port` |
-| `sentry` | Send data into Sentry | * `dsn` | |
-| `twilio` | Send via SMS | * `sid`
* `token`
* `to`
* `from` | |
-| `http`/`https` | Send data via http/https | * `url` | * `httpMethod`
* `x` where x is any query parameter |
+| id | Description | Required properties | Optional properties |
+|------------------|---------------------------------|------------------------------------------------------------| -------------------------------------------------------- |
+| `stdout` | Write all data to standard out. | None. | |
+| `slack` | Send data to a Slack channel | * `webhook`
* `channel` | |
+| `fluentd` | Sent data to FluentD | * `tag`
* `host`
* `port` | |
+| `smtp`/`smtps` | Sent data via email | * `server`
* `subject`
* `to`
* `from` | * `port` |
+| `sentry` | Send data into Sentry | * `dsn` | |
+| `twilio` | Send via SMS | * `sid`
* `token`
* `to`
* `from` | |
+| `loki` | Send data into Grafana Loki | * `url`
* `username`
* `password`
| |
+| `http`/`https` | Send data via http/https | * `url` | * `httpMethod`
* `x` where x is any query parameter |
### Example
@@ -46,7 +47,7 @@ type=stdout
[sentry_out]
type=sentry
-dsn=
+dsn=
[twilio_sms]
type=twilio
@@ -55,6 +56,12 @@ token=x
to=+x
from=+x
+[loki_out]
+type=loki
+url=
+username=
+password=
+
[slackme]
type=slack
channel=open-source-system-status
diff --git a/docs/config/examples/file.md b/docs/config/examples/file.md
index d995871..5127747 100644
--- a/docs/config/examples/file.md
+++ b/docs/config/examples/file.md
@@ -1,8 +1,10 @@
+# File
+
This example shows how to use `file` as a data source.
## Configuration
-Be sure to have read up on [Manzan configuration](/config/index.md) to understand where this files exist on your system.
+Be sure to have read up on [Manzan configuration](/config/index.md) to understand where these files exist on your system.
### `app.ini`
diff --git a/docs/config/examples/grafanaLoki.md b/docs/config/examples/grafanaLoki.md
new file mode 100644
index 0000000..3b14aa5
--- /dev/null
+++ b/docs/config/examples/grafanaLoki.md
@@ -0,0 +1,34 @@
+# Grafana Loki
+
+This example shows how to use `loki` as a destination for a `watch` data source.
+
+## Configuration
+
+Be sure to have read up on [Manzan configuration](/config/index.md) to understand where these files exist on your system.
+
+### `data.ini`
+
+```ini
+type=watch
+id=sanjula
+destinations=loki_out
+format=$MESSAGE_ID$ (severity $SEVERITY$): $MESSAGE$
+strwch=WCHMSG((*ALL)) WCHMSGQ((*HSTLOG))
+```
+
+### `dests.ini`
+
+```ini
+[loki_out]
+type=loki
+url=
+username=
+password=
+```
+
+## Result
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/config/examples/sentry.md b/docs/config/examples/sentry.md
index 54e178a..58db9fa 100644
--- a/docs/config/examples/sentry.md
+++ b/docs/config/examples/sentry.md
@@ -1,8 +1,10 @@
-This example shows how to use `sentry` as a destination for a `watch` data source
+# Sentry
+
+This example shows how to use `sentry` as a destination for a `watch` data source.
## Configuration
-Be sure to have read up on [Manzan configuration](/config/index.md) to understand where this files exist on your system.
+Be sure to have read up on [Manzan configuration](/config/index.md) to understand where these files exist on your system.
### `data.ini`
@@ -20,11 +22,11 @@ strwch=WCHMSG((*ALL)) WCHMSGQ((*HSTLOG))
```ini
[sentry_out]
type=sentry
-dsn=
+dsn=
```
## Result
-![](./images/sentry1.png)
+![](../../images/sentry1.png)
-![](./images/sentry2.png)
\ No newline at end of file
+![](../../images/sentry2.png)
\ No newline at end of file
diff --git a/docs/config/examples/twilio.md b/docs/config/examples/twilio.md
index a917adb..2ed2058 100644
--- a/docs/config/examples/twilio.md
+++ b/docs/config/examples/twilio.md
@@ -1,8 +1,10 @@
+# Twilio
+
This example shows how to use `twilio` as a destination.
## Configuration
-Be sure to have read up on [Manzan configuration](/config/index.md) to understand where this files exist on your system.
+Be sure to have read up on [Manzan configuration](/config/index.md) to understand where these files exist on your system.
### `data.ini`
@@ -40,4 +42,4 @@ from=+12058135364
## Result
-![](./images/twilio.png)
\ No newline at end of file
+![](../../images/twilio.png)
\ No newline at end of file
diff --git a/docs/images/grafanaLoki1.png b/docs/images/grafanaLoki1.png
new file mode 100644
index 0000000..a4aaf45
Binary files /dev/null and b/docs/images/grafanaLoki1.png differ
diff --git a/docs/images/grafanaLoki2.png b/docs/images/grafanaLoki2.png
new file mode 100644
index 0000000..834cf07
Binary files /dev/null and b/docs/images/grafanaLoki2.png differ
diff --git a/docs/config/examples/images/sentry1.png b/docs/images/sentry1.png
similarity index 100%
rename from docs/config/examples/images/sentry1.png
rename to docs/images/sentry1.png
diff --git a/docs/config/examples/images/sentry2.png b/docs/images/sentry2.png
similarity index 100%
rename from docs/config/examples/images/sentry2.png
rename to docs/images/sentry2.png
diff --git a/docs/config/examples/images/twilio.png b/docs/images/twilio.png
similarity index 100%
rename from docs/config/examples/images/twilio.png
rename to docs/images/twilio.png