在巨大的日志文件中搜索数百种模式

searching for hundreds of patterns in huge Logfiles

本文关键字:模式 百种 搜索 巨大 日志 文件      更新时间:2023-10-16

我必须从web服务器的htdocs目录中获得大量的文件名,然后使用这个文件名列表来搜索大量的存档日志文件,以便最后访问这些文件。

我计划用Boost在c++中做这件事。我会先取最新的日志,然后倒着读,检查每一行是否有我得到的所有文件名。

如果文件名匹配,我从Logstring读取时间并保存它的最后一次访问。现在我不需要再查找这个文件了,因为我只想知道最后一次访问。

要搜索的文件名向量应该会迅速减少。

我想知道如何用多线程最有效地处理这种问题。

我分区日志文件,让每个线程从内存中搜索日志的一部分,如果一个线程有一个匹配,它从文件名向量中删除这个文件名,或者有一个更有效的方法来做到这一点?

尝试使用mmap,它将节省你相当多的脱发。我觉得自己很匆忙,而且心情有些奇怪,所以我写了一个简单的东西让你开始。希望这对你有帮助!

mmap的美妙之处在于它可以很容易地与OpenMP并行。这也是防止I/O瓶颈的好方法。让我先定义Logfile类,然后再讨论实现。

头文件(logfile.h)

#ifndef _LOGFILE_H_
#define _LOGFILE_H_
#include <iostream>
#include <fcntl.h>
#include <stdio.h>
#include <string>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
using std::string;
class Logfile {
public:
    Logfile(string title);
    char* open();
    unsigned int get_size() const;
    string get_name() const;
    bool close();
private:
    string name;
    char* start;
    unsigned int size;
    int file_descriptor;
};
#endif

这是。cpp文件。

#include <iostream>
#include "logfile.h"
using namespace std;
Logfile::Logfile(string name){
    this->name = name;
    start = NULL;
    size = 0;
    file_descriptor = -1;
}
char* Logfile::open(){
    // get file size
    struct stat st;
    stat(title.c_str(), &st);
    size = st.st_size;
    // get file descriptor
    file_descriptor = open(title.c_str(), O_RDONLY);
    if(file_descriptor < 0){
        cerr << "Error obtaining file descriptor for: " << title.c_str() << endl;
        return NULL;
    }
    // memory map part
    start = (char*) mmap(NULL, size, PROT_READ, MAP_SHARED, file_descriptor, 0);
    if(start == NULL){
        cerr << "Error memory-mapping the filen";
        close(file_descriptor);
        return NULL;
    }
    return start;
}
unsigned int Logfile::get_size() const {
    return size;
}
string Logfile::get_title() const {
    return title;
}
bool Logfile::close(){
    if( start == NULL){
        cerr << "Error closing file. Was closetext() called without a matching opentext() ?n";
        return false;
    }
    // unmap memory and close file
    bool ret = munmap(start, size) != -1 && close(file_descriptor) != -1;
    start = NULL;
    return ret;
}

现在,使用这段代码,您可以使用OpenMP工作共享这些日志文件的解析,即

Logfile lf ("yourfile");
char * log = lf.open();
int size = (int) lf.get_size();
#pragma omp parallel shared(log, size) private(i)
{
  #pragma omp for
  for (i = 0 ; i < size ; i++) {
     // do your routine
  }
  #pragma omp critical
     // some methods that combine the thread results
}

将日志文件解析为数据库表(SQLite ftw)。其中一个字段将是路径。

在另一个表中,添加您正在查找的文件。

现在它是派生表上的一个简单连接。像这样。

SELECT l.file, l.last_access FROM toFind f
LEFT JOIN ( 
    SELECT file, max(last_access) as last_access from logs group by file
) as l ON f.file = l.file

toFind中的所有文件都将在那里,并且对于那些在日志中没有找到的文件将具有last_access NULL。

好吧,这是几天前的事了,但我花了一些时间写代码,并在其他项目中使用SQLite。

我仍然想在性能方面比较db方法和MMAP解决方案。

当然,如果你可以使用sql查询来处理你解析的所有数据,它可以节省你很多的工作。但我真的不关心工作量,因为我还在学习很多东西,我从中学到的是:

这个mmap方法——如果你正确地实现它——在性能上绝对是优越的。这是令人难以置信的快,如果你实现"单词计数"的例子,你会注意到,这可以看作是MapReduce算法的"hello world"。

现在,如果你想进一步从sql语言中获益,正确的方法是实现你自己的sql包装器,它也使用Map-Reduce的方式在线程之间共享查询。

你也许可以在线程之间通过ID共享对象,每个线程处理它自己的DB-Connection。然后,它在自己的数据集中查询对象。

这将比通常的方式写东西到SQLite DB要快得多。

毕竟你可以说:

MMAP是处理字符串处理的最快方式SQL为解析器应用程序提供了强大的功能,但如果不实现处理SQL查询的包装器,它会减慢速度