Giter Club home page Giter Club logo

vertx-web-accesslog's Introduction

Build Status (5.x) Build Status (4.x) Coverage Awesome

vertx-web-accesslog

An access log implementation to be used in vertx web routes.

Inspired and with intention to be compliant with

The main idea is to have an event based logging, with the possibility (but not enforcing you to do so) to directly export a log event natively to a target system, and not having to first write and persist it to a file and then read and parse it again from there like eg ELK is doing it. The drawback that those kind of solutions have is performance but even more issues like recognizing complete stacktraces etc. The ElasticSearch Appender is the only appender for now that takes advantage of those benefits. However you are free of course to use a traditional ELK setup style and just write your output to either console or a specific logfile - and let eg logstash take over from there.

Key facts

  • Zero dependencies (apart of vertx-web obviously)
  • Easily extendable and customizable
  • Small memory footprint

Technical Usage

The artefact is published on maven central.

Just add it as a dependency to your project (gradle example)

dependencies {
	compile 'com.romanpierson:vertx-web-accesslog:1.7.0'
}

Compatibility with Vertx core

Accesslog version Vertx version
5.0.0-SNAPSHOT 5.0.0-SNAPSHOT >
1.7.0 4.5.1 >
1.6.0 4.3.0 >
1.5.0 4.2.0 >
1.4.0 4.0.0 - 4.1.x
1.3.1 3.3.0 - 3.7.0
1.2.0 3.3.0 - 3.7.0

Previous versions are listed for completeness only and not supported anymore.

Access Log Pattern Configuration

The logger supports mixing of both log formats and is also designed to easily add custom log elements

Define your custom AccessLogElement

You can easily create your custom implementation of AccessLogElement by creating your own element class implementing AccessLogElement interface. The available AccessLogElement types are discovered by ServiceLoader, so just add to your resources a file like this and inside list your AccessLogElement classes

META-INF
 services
  com.romanpierson.vertx.web.accesslogger.configuration.element.AccessLogElement

As ServiceLoader SPI is intented to work with objects unfortunately your AccessLogElement implementation requires a parameter less constructor.

In order that the pattern resolver is able to resolve your new element against a pattern you have to implement your resolving condition by implementing findInRawPatternInternal(rawPattern) method.

To facility this and to avoid boilerplate code there are static helpers in PatternResolver that simplifies that a lot.

Method Resolves pattern Remarks Examples
extractBestPositionFromFixPatternIfApplicable <VALUE> Whatever value is matched and resolved %b cs-uri
extractBestPositionFromFixPatternsIfApplicable <VALUE> Like above but you can pass a list of values to be matched %b cs-uri
extractBestPositionFromPostfixPatternIfApplicable %{<CONFIGURATION>}POSTFIX Searches for a postfixed pattern and extracts a configuration string that is passed later to your defined function %{msec}t
extractBestPositionFromPostfixPatternAndAdditionalCheckIfApplicable %{<CONFIGURATION>}POSTFIX Like above but let you define an additional function that checks if the found configuration value is valid for your element to be handled %{msec}t

Redefine existing elements with your custom one

By defining your custom element using same pattern as an existing one shipped with this library it will have preference over the predefined one.

Appenders

Appenders are basically a way to send the log data to one (or multiple) backends. This ships with a set of (hopefully) useful appenders but you can create your custom appenders in a very easy way.

Available Appenders

Appender Description
Console Appender Embedded - main purpose for testing
EventBus Appender Embedded - simple way to forward access events to a configurable address on the event bus
Logging Appender Embedded - Using common logging functionality (logback, slf4j, etc)
ElasticSearch Appender Embedded - Experimental appender that writes data to ElasticSearch (For usage eg in kibana) Requires Vertx ElasticSearch Indexer

Custom Appenders

You can easily write your own appender doing

Write your own CustomAppender class that must

  • implement Appender Interface
  • have a public constructor taking a JsonObject instance holding the configuration

AccessLoggerProducerVerticle

This verticle is responsible for receiving the raw data, formatting it based on the AccessLogElements configured and forwards the resulting data to the registered Appenders (by calling Appender.push).

There is one worker instance of AccessLoggerProducerVerticle per vertx context started if you put configuration value isAutoDeployProducerVerticle to true (by default it is). If you prefer to manage the deployment of that verticle byself set the property to false.

Usage

This describes the basic usage. More specific info eg about the different appenders can be found on the links.

Configure route

Just put an instance of AccessLogHandler as first route handler.

Router router = Router.router(vertx);

JsonObject config = .... load or create your configuration json

router.route().handler(AccessLoggerHandler.create(config));

As configuration is now done by plain JsonObject its very simple to use and inject configuration eg by yaml, see as an example ServerSetupStarter

configurations:
  - identifier: accesslog-formatted
    logPattern: '%{}t %D "cs-uri"'
    appenders:
      - appenderClassName : com.romanpierson.vertx.web.accesslogger.appender.console.impl.ConsoleAppender
  - identifier: accesslog-plain
    logPattern: "%{msec}t %D cs-uri"
    appenders:
      - appenderClassName : com.romanpierson.vertx.web.accesslogger.appender.console.impl.ConsoleAppender

Supported log elements

Currently those elements are supported

Element Apache W3C Remarks
Method %m cs-method
Status %s sc-status
Duration s %T -
Duration ms %D -
Remote Host %h -
Local Host %v -
Local port %p -
Bytes Written v1 %B - Zero Bytes written as 0
Bytes Written v2 %b - Zero Bytes written as -
First line of request %r -
URI path only %U cs-uri-stem
Query only %q cs-uri-query
URI path incl query - cs-uri
Version / Protocol %H -
Datetime Apache %t - Logs by default the request timestamp using format 'EEE, dd MMM yyyy HH:mm:ss zzz', Locale English and Timezone GMT
Datetime Apache Timeunit %t{msec} - Currently only milliseconds is supported
Datetime Apache Configurable v1 %{PATTERN}t - Specify the format pattern, by default it is used Locale English and Timezone GMT
Datetime Apache Configurable v2 %{PATTERN|TIMEZONE|LANGUAGE}t - Specify format pattern, timezone and language
Incoming Headers %{IDENTIFIER}i -
Outgoing Response Headers %{IDENTIFIER}o -
Cookie %{IDENTIFIER}C - Request cookies only
Static value %{IDENTIFIER}static -
Environment Variable value %{IDENTIFIER}env -

Static values

For static values you should prefer to use the %{value}static element. In case you have an appender like ConsoleAppender or LoggingAppender that writes its output via the resolved pattern you can also put such static values directly into the logpattern as it will just stay as non resolved. However for other appenders like ElasticSearchAppender one you need to explicitly define the element.

Empty behavior

The default way for elements where no actual value can be evaluated is to return a NULL value. This way the appender is able to translate this into an empty string or eg skip the value if we index towards a solution like ElasticSearch.

Changelog

Detailed changelog can be found here.

Demo Playground

A sample project that shows usage of this (and other related features) can be found here.

vertx-web-accesslog's People

Contributors

romanpierson avatar romanpiersonosudio avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

vertx-web-accesslog's Issues

No license information

Hi Roman,

I am interesting in using vertx-web-accesslog but I notice that you have no licensing information.

If you agree would it be possible to get a standard MIT license or failing that an Apace license added to LICENSE.txt in the root folder ?

Many thanks

PatternResolver fails to match all elements depending on order

I found an issue when re-ordering the elements in the log output pattern. If you change the logPattern in the server-config.yaml file to be "%m %D cs-uri" the example application crashes.

The issue appears to be caused by failing to resolve all the pattern elements in com.mdac.vertx.web.accesslogger.configuration.pattern.PatternResolver#resolvePattern. I've raised a pull request that adds a unit test and attempts to fix the issue by moving the offset check to only run if the extracted position is == to the previously found element.

I wasn't certain of the purpose of checking the offset, perhaps it can be ignored completely?

Happy to hear any comments.

Pull request: #10

New version for 3.5 vertx?

I noticed you made commits for 3.5 vertx, will that new version be published on bintray? Looks like the version on bintray is from before such changes

Sporadic initialization issues

Hello everyone,

Sometimes when deploying an vertx application (AWS ECS/Fargate) the initialization fails for on obvious reason.

hocon-config is pretty much similar to the one provided in this repos documentation:

configurations = [
  {
    identifier: accesslog-formatted
    logPattern: "%H cs-method \"cs-uri\" HTTP/sc-status took %Dms %Bbytes"
    appenders = [
      {
        appenderClassName: com.romanpierson.vertx.web.accesslogger.appender.logging.impl.LoggingAppender
        config {
          loggerName: accesslog
        }
      }
    ]
  }
]

Exception:

com.romanpierson.vertx.web.accesslogger.exception.AccessLoggerException: Unable to register access log configuration [accesslog-formatted]
	at com.romanpierson.vertx.web.accesslogger.impl.AccessLoggerHandlerImpl.lambda$new$0(AccessLoggerHandlerImpl.java:131)
	at io.vertx.core.impl.future.FutureImpl$3.onFailure(FutureImpl.java:153)
	at io.vertx.core.impl.future.FutureBase.emitFailure(FutureBase.java:75)
	at io.vertx.core.impl.future.FutureImpl.addListener(FutureImpl.java:191)
	at io.vertx.core.impl.future.PromiseImpl.addListener(PromiseImpl.java:23)
	at io.vertx.core.impl.future.FutureImpl.onComplete(FutureImpl.java:164)
	at io.vertx.core.impl.future.PromiseImpl.onComplete(PromiseImpl.java:23)
	at io.vertx.core.eventbus.EventBus.request(EventBus.java:107)
	at io.vertx.core.eventbus.EventBus.request(EventBus.java:85)
	at com.romanpierson.vertx.web.accesslogger.impl.AccessLoggerHandlerImpl.lambda$new$1(AccessLoggerHandlerImpl.java:98)
	at java.base/java.lang.Iterable.forEach(Iterable.java:75)
	at com.romanpierson.vertx.web.accesslogger.impl.AccessLoggerHandlerImpl.<init>(AccessLoggerHandlerImpl.java:90)
	at com.romanpierson.vertx.web.accesslogger.AccessLoggerHandler.create(AccessLoggerHandler.java:39)
	at de.thisismana.VertexWithAccessLogs.lambda$start$2(VertexWithAccessLogs.java:66)
	at io.vertx.core.impl.future.FutureImpl$3.onSuccess(FutureImpl.java:141)
	at io.vertx.core.impl.future.FutureImpl$ListenerArray.onSuccess(FutureImpl.java:262)
	at io.vertx.core.impl.future.FutureBase.emitSuccess(FutureBase.java:60)
	at io.vertx.core.impl.future.FutureImpl.tryComplete(FutureImpl.java:211)
	at io.vertx.core.impl.future.Mapping.onSuccess(Mapping.java:40)
	at io.vertx.core.impl.future.FutureBase.emitSuccess(FutureBase.java:60)
	at io.vertx.core.impl.future.FutureImpl.tryComplete(FutureImpl.java:211)
	at io.vertx.core.impl.future.Mapping.onSuccess(Mapping.java:40)
	at io.vertx.core.impl.future.FutureBase.emitSuccess(FutureBase.java:60)
	at io.vertx.core.impl.future.FutureImpl.tryComplete(FutureImpl.java:211)
	at io.vertx.core.impl.future.CompositeFutureImpl.trySucceed(CompositeFutureImpl.java:163)
	at io.vertx.core.impl.future.CompositeFutureImpl.lambda$all$0(CompositeFutureImpl.java:38)
	at io.vertx.core.impl.future.FutureImpl$3.onSuccess(FutureImpl.java:141)
	at io.vertx.core.impl.future.FutureBase.emitSuccess(FutureBase.java:60)
	at io.vertx.core.impl.future.FutureImpl.tryComplete(FutureImpl.java:211)
	at io.vertx.core.impl.future.Composition$1.onSuccess(Composition.java:62)
	at io.vertx.core.impl.future.FutureBase.emitSuccess(FutureBase.java:60)
	at io.vertx.core.impl.future.SucceededFuture.addListener(SucceededFuture.java:88)
	at io.vertx.core.impl.future.Composition.onSuccess(Composition.java:43)
	at io.vertx.core.impl.future.FutureBase.emitSuccess(FutureBase.java:60)
	at io.vertx.core.impl.future.FutureImpl.tryComplete(FutureImpl.java:211)
	at io.vertx.core.impl.future.Composition$1.onSuccess(Composition.java:62)
	at io.vertx.core.impl.future.FutureImpl$ListenerArray.onSuccess(FutureImpl.java:262)
	at io.vertx.core.impl.future.FutureBase.lambda$emitSuccess$0(FutureBase.java:54)
	at io.netty.util.concurrent.AbstractEventExecutor.runTask(AbstractEventExecutor.java:174)
	at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:167)
	at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:470)
	at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:569)
	at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997)
	at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
	at java.base/java.lang.Thread.run(Thread.java:833)
Caused by: (NO_HANDLERS,-1) No handlers for address accesslog.event.register
	at io.vertx.core.eventbus.impl.EventBusImpl.deliverMessageLocally(EventBusImpl.java:384)
	at io.vertx.core.eventbus.impl.EventBusImpl.sendLocally(EventBusImpl.java:341)
	at io.vertx.core.eventbus.impl.EventBusImpl.sendOrPub(EventBusImpl.java:329)
	at io.vertx.core.eventbus.impl.OutboundDeliveryContext.execute(OutboundDeliveryContext.java:109)
	at io.vertx.core.eventbus.impl.DeliveryContextBase.next(DeliveryContextBase.java:72)
	at io.vertx.core.eventbus.impl.OutboundDeliveryContext.next(OutboundDeliveryContext.java:28)
	at io.vertx.core.eventbus.impl.EventBusImpl.sendOrPubInternal(EventBusImpl.java:422)
	at io.vertx.core.eventbus.impl.EventBusImpl.sendOrPubInternal(EventBusImpl.java:428)
	at io.vertx.core.eventbus.impl.EventBusImpl.request(EventBusImpl.java:127)
	at io.vertx.core.eventbus.EventBus.request(EventBus.java:106)
	... 38 more

Usually a restart fixes things. But access logging will permanently fail for affected instances with the following error log:

[AccessLoggerHandlerImpl] Handler not ready to log due to missing registration(s)

The order of initialization is like this. So config should be ready at this point:

        ConfigStoreOptions store = new ConfigStoreOptions()
            .setType("file")
            .setFormat("hocon")
            .setConfig(new JsonObject().put("path", "application.conf"));

        ConfigRetriever retriever = ConfigRetriever.create(vertx,
            new ConfigRetrieverOptions().addStore(store));

        retriever.getConfig()
            .onFailure(log::error)
            .onComplete(json -> {
                var config = json.result();
                router.route().handler(AccessLoggerHandler.create(config));

Any idea what might cause this behavior (like a race condition)? Unfortunately, I'm quite new to vertx and cannot reproduce it on my machine nor when starting the very same docker image used on the production system.

How do I use this?

  • Is this published on maven or some other artifact repository?
  • If so, how could I configure it as a dependency.

I looked at the build.gradle but it doesn't show any group:artifact:version info

1.0.0 incompatibility with Vertx 3.8.4 -- More inquiry than issue

Hey, thanks for creating the access logger handler.

This is more of an inquiry than an issue, but I've been using 1.0.0 for a long while now and I have seen no need to upgrade to the latest version yet. However, I've been looking to upgrade to Vertx 3.8.4 and in that version, they got rid of the io.vertx.ext.web.impl.Utils.createRFC1123DateTimeFormatter() method which is still used in a few places in the 1.0.0 version of this package. So I was thinking of upgrading to the latest version, but it seems to me that there are some pretty major changes that I am unsure if I want to take on i.e use of a worker verticle and event bus to facilitate logging.

Since I'm perfectly content with 1.0.0 except for the no longer valid method from Vertx Utils, I am tempted to fork the old version and replace the invalid method usage. But before I do that, I want to inquire if there are any stronger reasons why it would be good to use the latest version instead?

Thanks a lot!

NoSuchMethodError after creating singleton verticle log

Hi, I tried to follow your example to implement access logs on a vertix project and I have this error :

SEVERE: Unhandled exception
java.lang.NoSuchMethodError: io.vertx.core.Vertx.deployVerticle(Ljava/lang/String;Lio/vertx/core/DeploymentOptions;)Lio/vertx/core/Future;

I have added the yaml configuration you had in your readme like this :

public static void main(final String[] args) {
Launcher.executeCommand("run", MainVerticle.class.getName());

}

//@OverRide
public void start(Future startFuture) throws Exception {

JsonObject config= new JsonObject()
  .put("path", "src/main/conf/configuration.yaml");

ConfigStoreOptions store = new ConfigStoreOptions()
  .setType("file")
  .setFormat("yaml")
  .setConfig(config);

ConfigRetriever retriever = ConfigRetriever.create(vertx,
  new ConfigRetrieverOptions().addStore(store));



retriever.getConfig(json -> {
  JsonObject result = json.result();
  Router router = Router.router(vertx);
  router.route().handler(AccessLoggerHandler.create(result));
  HttpServer server = vertx.createHttpServer();

  server.requestHandler(request -> {

    // This handler gets called for each request that arrives on the server
    HttpServerResponse response = request.response();
    response.putHeader("content-type", "text/plain");

    // Write to the response and end it
    response.end("Hello World!");
  });

  server.listen(8080);
});

Thanks

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.