Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fast access logging for analytics #537

Open
krizhanovsky opened this issue May 27, 2016 · 5 comments
Open

Fast access logging for analytics #537

krizhanovsky opened this issue May 27, 2016 · 5 comments
Assignees
Labels
crucial enhancement TDB Tempesta DB module and related issues
Milestone

Comments

@krizhanovsky
Copy link
Contributor

krizhanovsky commented May 27, 2016

Now all information from config file parsing errors to clients blocking is written to dmesg. Instead following logs on top of TempestaDB must be introduced:

  • access log - TfwHttpReq must be passed as an argument to tfw_log_access() function.
  • security log - all security event must be logged there (e.g. block client IP, Frang rate limiting etc.). It seems there could be several logging functions which write events at different layers. Since we log blocking events, the events might overflow the log due to DoS attack. At the moment they're logged using net_warn_ratelimited(), so we're good with the system ratelimiter. We should either leave the security log in the kernel log or implement our own rate limiter.
  • error log - all HTTP processing events must go there (e.g. invalid URI), records in the log also must be done in TfwHttpReq context;
  • probably stress (performance) events.

The 2 modes of logging must be implemented:

  1. TDB tables with automatic eviction of old records (ring buffer) to fit in RAM
  2. logs transmission to a remote server using TCP synchronous sockets (not to lose records under peak load)

The logs should be configured by independent configuration options:

    log_access /opt/tempesta/db/log_access.tdb <variables>
    log_error /opt/tempesta/db/log_error.tdb <variables>
    log_security 192.168.1.1:5000 <variables>

variables is the list of variables to log:

  1. remote_addr - remote user IP
  2. time - local time (miliseconds since epoch)
  3. method - request method
  4. uri
  5. resp_status - response status
  6. body_sent - response bytes sent
  7. resp_time - response time
  8. values of any special header with srvhdr_ or clntcdr_ prefixes and - changed to _, e.g. srvhdr_set_cookie or clnt_user_agent
  9. cache miss/hit
  10. origin IP

In general, Tempesta DB should provide streaming data processing (#515 and #516) foundation for the logging application. Probably we need to keep all request and response headers with the metadata (e.g. timings, chunks information, TCP/IP and TLS layers etc) for a relatively short sliding window. Such data is extremely useful to query to debug immediate performance issues and DDoS attacks.

TBD a possible application is to batch events in front of a time series DB, e.g. ClickHouse, InfluxDB or TimescaleDB.

We need to support per-vhost logging, i.e. thousands of vhosts having several logs each. This could be done using either secondary index #733 or we have to be able to scale to thousands of TDB tables.

For better performance logs must use simple sequential ring-buffer TDB table format w/o any indexes (#516). Log records must be stored in structured TDB records. Probably we don't event need TDB for this and just mmap the ring buffer into the user space.

The binary log format could be

    <event_type><timestamp><var0><var1>...<varN>

, where event_type defines the event type and it's format (number of variables and their type, e.g. client address, URI, HTTP Host header etc.). In this release the format must be simple and hardcoded.

Simple retrieval user space tool like varnishlog must be developed on top of tdbq to print the logs and/or write them in human-readable or JSON formats to files. The tool also must be able to run in daemon mode, read the TDB tables and flush the log records to files or syslogd.

The human-readable text format should be compatible with the W3C draft, but should also provide more information.

Also reference TUX and HAProxy, which also use(ed) binary logging.

@krizhanovsky krizhanovsky added this to the 0.5.0 Web Server milestone May 27, 2016
@krizhanovsky krizhanovsky changed the title Logging Custom logging May 27, 2016
@krizhanovsky krizhanovsky modified the milestones: 0.6 WebOS, 0.5.0 Web Server Feb 23, 2017
@krizhanovsky krizhanovsky modified the milestones: backlog, 0.11 Tempesta Language Jan 15, 2018
@krizhanovsky krizhanovsky modified the milestones: 1.5 Tempesta Language, 1.3 Web server Aug 8, 2018
@krizhanovsky krizhanovsky modified the milestones: 1.3 Web server & advanced strings, 1.0 Beta Feb 2, 2019
@krizhanovsky krizhanovsky modified the milestones: 1.0 Beta, 1.1 Network performance & scalability, 1.1 TBD (Network performance & scalability), 1.1 TDB (ML, QUIC, DoH etc.) Feb 11, 2019
@krizhanovsky krizhanovsky added the TDB Tempesta DB module and related issues label Apr 27, 2020
@krizhanovsky krizhanovsky self-assigned this Dec 28, 2021
@krizhanovsky krizhanovsky modified the milestones: 0.8 - TBD, 1.1 - TLS 1.3 Jan 3, 2022
@krizhanovsky
Copy link
Contributor Author

krizhanovsky commented Jan 18, 2024

At the moment there are hundreds of log messages of various levels and generic printf()-like formats. Hopefully, all of them are printed with macros like T_ERR or T_WARN, so all of them can be preprocessed by a tool, which will build a C table with indexes and compiled formats to avoid formats conversion in runtime. See qrintf

One more approach is to log only binary data (e.g. integers, floats and nanoseconds since epoch) into a ring buffer and use a log reading tool to process the binary data. This is very close to what HAproxy does https://www.youtube.com/watch?v=762owEyCI4o

Access log is a separate case: it can be used to compute an advanced statistics - larger log allows longer statistics. E.g. with the current access log we can compute statistics for each of return code and we don't actually need the counters implemented in #2023 (#1454). Access log can be also extended with:

  • was a response got from cache or requested from an upstream
  • size of request to estimate number of bytes sent to an upstream)

Probably #2023 should be reverted, but maybe we should provide an application layer parser, which will compute the statistics. This probably can be done with the same library as tdbq, see #279 and #528.

@krizhanovsky krizhanovsky removed their assignment Sep 28, 2024
@krizhanovsky krizhanovsky modified the milestones: 1.2 - TBD, 0.9 - LA Sep 28, 2024
@krizhanovsky krizhanovsky changed the title Custom logging Fast access logging for analytics Sep 28, 2024
@krizhanovsky
Copy link
Contributor Author

krizhanovsky commented Sep 28, 2024

The problem is crucial because of low performance of the kernel log and absence of analytics abilities for DDoS incident responses.

To react on a DDoS incident we need to extend the access log with JA5 fingerprints #2052.

Access log must have a per-cpu user-space mapped (see for example how tcp_mmap maps user-space pages) ring buffers. (Please check the current state of the generic ringbuffers and make a TODO comment and probably and issue to use it. Also see it's implementation to borrow some code.) access_log configuration option must be extended with a size of a per-cpu buffer (1MB minumum and by default) and the current mode: dmesg or mmap. The access log records must be written in a binary format (string parameters must use 2 byte length and be truncated by 64KB). Truncated strings must have explicit attribute of truncation.

The log records and the whole mmaped buffers must be defined with a C structure, which will be later extended, e.g with a record type for error and security events. The buffer structures must have two integer fields: head and tail. head is the offset of the last written byte by sotfirq. tail is the next byte to read by a user-space daemon. If there is not enough space to insert a new log record, softirq must account the number of skipped records. The next inserted record should contain the counter in the descriptor data structure.

A C++ daemon must spawn N threads, where N is defined as a command line argument. During startup the daemon should define the sets of CPUs, which each of the threads should process. The daemon must use the ClickHouse client library to connect to a ClickHouse instance by an address, specified in another command line argument. Each thread must process the mmaped buffers of assigned CPUs in round-robin fashion and prepare ClickHouse batches of configured size and send to ClickHouse. A code example from ChatGPT:

#include <clickhouse/client.h>
using namespace clickhouse;

int main() {
    // Establish connection to ClickHouse server
    Client client(ClientOptions().SetHost("localhost"));

    // Define batch of data
    Block block;
    block.AppendColumn("column1", std::make_shared<ColumnUInt64>());
    block.AppendColumn("column2", std::make_shared<ColumnString>());

    // Insert rows to the block
    for (uint64_t i = 0; i < 1000; ++i) {
        block[0]->As<ColumnUInt64>()->Append(i);
        block[1]->As<ColumnString>()->Append("value" + std::to_string(i));
    }

    // Insert the batch into ClickHouse
    client.Insert("default.my_table", block);

    return 0;
}

If a thread reaches head of all designated log buffers, it should sleep for a short period of time, e.g. 0.1s or 0.01s (futexes aren't available for us).

All the log records must contain current timestamp (like jiffies), which must be light-weight to get and be accessible from the user space. There is no need to sort the records - leave it for ClickHouse.

The daemon should use C++23 and Boost, but not asio due it's poor performance.

The daemon is supposed to be extended to write to a file, but for now let's just keep the current dmesg implementation for this case.

Testing & doc

Please update the wiki.

The testing issue is #2269

@krizhanovsky
Copy link
Contributor Author

Also, for slow DDoS attacks detection, let's add time spent to send response to the access log, like <response bytes sent>/<time in seconds>. Good to start with and make in a separate pull request.

@krizhanovsky krizhanovsky modified the milestones: 0.9 - LA, 0.8 - Beta Sep 30, 2024
ai-tmpst added a commit that referenced this issue Oct 10, 2024
In #537 we need a way to deliver log data to userspace. Introduce a set of
per-cpu ring buffer mapped to userspace.

Signed-off-by: Alexander Ivanov <[email protected]>
ai-tmpst added a commit that referenced this issue Oct 10, 2024
In #537 we need a way to deliver log data to userspace. Introduce a set of
per-cpu ring buffer mapped to userspace.

Signed-off-by: Alexander Ivanov <[email protected]>
@ai-tmpst ai-tmpst linked a pull request Oct 10, 2024 that will close this issue
ai-tmpst added a commit that referenced this issue Oct 21, 2024
In #537 we need a way to deliver log data to userspace. Introduce a set of
per-cpu ring buffer mapped to userspace.

Signed-off-by: Alexander Ivanov <[email protected]>
ai-tmpst added a commit that referenced this issue Oct 30, 2024
In #537 we need a way to deliver log data to userspace. Introduce a set of
per-cpu ring buffer mapped to userspace.

Signed-off-by: Alexander Ivanov <[email protected]>
@ai-tmpst
Copy link
Contributor

For access log table creation connect by clickhouse client and execute:
CREATE TABLE IF NOT EXISTS access_log (timestamp DateTime64, address IPv6, method UInt8, version UInt8, status UInt16, response_content_length UInt32, response_time UInt32, vhost String, uri String, referer String, user_agent String, dropped_events UInt64, PRIMARY KEY(timestamp))

@krizhanovsky
Copy link
Contributor Author

@ai-tmpst this table creation should be either in our Wiki installation guide or client handling or in Tempesta scripts

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
crucial enhancement TDB Tempesta DB module and related issues
Projects
None yet
Development

No branches or pull requests

3 participants