【问题标题】:Compressed apache logs in SqliteSqlite 中的压缩 apache 日志
【发布时间】:2021-09-23 20:16:49
【问题描述】:

我希望能够使用 SQL 语法查询我的 Apache 日志,其方式与工具 asql 类似。 我正在使用以下代码将 Apache 日志导入 Sqlite:

import sqlite3, apache_log_parser  # pip install apache_log_parser
conn = sqlite3.connect('logs.db')
cur = conn.cursor()
cur.execute("""CREATE TABLE IF NOT EXISTS logs (server TEXT, port INTEGER, ip TEXT, time TEXT, url TEXT, status INTEGER, bytes INTEGER, referer TEXT, useragent TEXT)""")
parser = apache_log_parser.make_parser("%v:%p %h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"")
with open("other_vhosts_access.log") as f:
    for line in f:
        d = parser(line)
        cur.execute("""INSERT INTO logs VALUES (:server_name, :server_port, :remote_host, :time_received_isoformat, :request_url, :status, :bytes_tx, :request_header_referer, :request_header_user_agent)""", d)
cur.close()
conn.commit()
conn.close()

它有效。然而,一个月的other_vhosts_access.log 200 MB 文件会产生近 200 MB 的 Sqlite DB 文件(没有压缩)。所以在我的情况下 1 年的日志:

  • 通常占用 500 MB:2 * 200 MB(上个月的 2 个纯文本)+ 10 * 10 MB(前 10 个月由 logrotate 压缩)

  • 现在占用:2.4 GB:12 * 200 MB

问题:有没有办法让logs.db(自动?)压缩,但仍然能够使用 Sqlite 运行只读的SELECT * FROM logs WHERE ... 查询?

我见过Sqlite ZIPVFS,但这不是开源的(而且对我的项目来说太贵了)。

【问题讨论】:

    标签: python apache sqlite logging compression


    【解决方案1】:

    JSON1 + 压缩 + 查看

    1. 编译compress(它是与旧的libsqlite3-dev 一起编译的先前版本,您可能需要安装zlib1g-dev)SQLite 扩展。默认情况下,JSON1 几乎总是已经存在。

      gcc -g -fPIC -shared compress.c -o compress.so
      
    2. listjson.dumps 列表中解析日志dicts,例如1024(因此压缩得很好),并将其传递给INSERT 其中compress 它。创建一个在 SQLite 侧执行相反操作的视图。

    这是表格和视图。

    import sqlite3
    
    conn = sqlite3.connect('/path/to/db.sqlite')
    conn.load_extension('/path/to/compress.so')
    conn.execute('''
        CREATE TABLE "log_block" (
            "log_block_id"  INTEGER PRIMARY KEY AUTOINCREMENT,
            "created_at"    DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
            "block"         BLOB NOT NULL
        )
    ''')
    conn.execute('''
        CREATE VIEW log_record AS
        SELECT 
            json_extract(value, '$.server') "server",
            json_extract(value, '$.port') "port",
            json_extract(value, '$.ip') "ip",
            json_extract(value, '$.time') "time",
            json_extract(value, '$.url') "url",
            json_extract(value, '$.status') "status",
            json_extract(value, '$.bytes') "bytes",
            json_extract(value, '$.referer') "referer",
            json_extract(value, '$.useragent') "useragent"
        FROM log_block, json_each(uncompress(log_block.block))
    ''')
    

    一个写入表格的例子是这样的。

    import itertools
    import json
    
    sample = {
        'server': 'foo.bar',
        'port': '80',
        'ip': '127.0.0.1',
        'time': '2013-08-16T15:45:34',
        'url': '/',
        'status': '200',
        'bytes': '42',
        'referer': 'https://example.com/',
        'useragent': 'Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.18)',
    }
    
    def chunk(iterable, n):
        return (
            tuple(filter(bool, c)) 
            for c in itertools.zip_longest(*[iter(iterable)] * n)
        )
    
    log_records = itertools.repeat(sample, 10_000)
    
    for c in chunk(log_records, 1024): 
        conn.execute(
            'INSERT INTO log_block(block) VALUES(compress(?))', 
            (json.dumps(c),)
        )
        conn.commit()
    

    然后像查询当前未压缩表一样查询视图。

    conn.execute('SELECT * FROM log_record LIMIT 1').fetchone()
    

    这对存档很有用,但检索不会很快(尽管对于您的数据量来说仍然应该没问题)。根据您的查询,您可以按某些字段对日志记录进行分组,例如url,而不是任意分块并将它们移动到log_block 表的列中。然后您就可以轻松索引它们了。

    SquashFS

    这仅适用于只读(历史时期)数据库。壁球FSprovides plenty of compression options:

    Squashfs 的原始版本使用gzip 压缩,虽然Linux 内核2.6.34 增加了对LZMALZO 压缩的支持,Linux 内核2.6.38 增加了对LZMA2 压缩的支持(xz 使用它) ),Linux 内核 3.19 增加了对LZ4 压缩的支持,Linux 内核 4.14 增加了对Zstandard 压缩的支持。

    这是一个示例(mksquashfs 来自 squashfs-tools 包)。

    $ mkdir databases
    $ sqlite3 databases/new.db \
      "CREATE TABLE foo(foo_id INT); INSERT INTO foo VALUES(123)"
    $ mksquashfs databases/ databases.squashfs -comp xz
    
    $ mkdir uncompressed
    $ sudo mount databases.squashfs uncompressed -t squashfs -o loop
    $ sqlite3 uncompressed/new.db "SELECT * FROM foo"
    123
    

    【讨论】:

      猜你喜欢
      • 2016-02-08
      • 1970-01-01
      • 1970-01-01
      • 2011-10-07
      • 1970-01-01
      • 2022-01-22
      • 2015-08-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多