`
RednaxelaFX
  • 浏览: 3053159 次
  • 性别: Icon_minigender_1
  • 来自: 海外
社区版块
存档分类
最新评论

一个生成伪log的程序……

    博客分类:
  • D
阅读更多
有个朋友让我帮忙写个伪log的生成器。他提供了一个源log文件和一个配置文件,要每隔一段时间就向一个指定的路径上的log文件添加一些新生成的log。

要求是:
1、从配置文件读入参数,根据配置来决定时间间隔与输出log的路径;
2、从源log文件得到生成log的材料;
3、随机从源log里挑选几行出来,把它开头的时间信息替换成当前时间;
4、以固定的时间间隔向目标路径添加新生成的log,并要求不在log文件的末尾生成空行。

配置文件类似这样:
RT_Config.inc:
[system]
//log format
Format=NCSA
//log of the dat
FileName=D:\temp\today.log
//port
Listen_Port=8000
//server IP
IP=127.0.0.1
//server port
Server_port=9000
//time interval (in SECs)
Time_Interval=20


然后源log文件类似这样(截取几行):
2008-03-31 00:00:19 10.11.14.56 - 10.11.1.9 80 GET /acip/shownewsh.asp - 200 Mozilla/4.0+(compatible;+MSIE+6.0;+Windows+NT+5.1;+SV1)
2008-03-31 00:00:20 10.11.14.56 - 10.11.1.9 80 GET /acip/images/Delighting+you+always+(Red)+789x382.gif - 304 Mozilla/4.0+(compatible;+MSIE+6.0;+Windows+NT+5.1;+SV1)
2008-03-31 00:00:47 10.11.10.81 - 10.11.1.9 80 GET /acip/shownewsh.asp - 200 Mozilla/4.0+(compatible;+MSIE+6.0;+Windows+NT+5.0)
2008-03-31 00:01:31 10.11.50.67 - 10.11.1.9 80 GET /acip/shownewsh.asp - 200 Mozilla/4.0+(compatible;+MSIE+6.0;+Windows+NT+5.1;+SV1)
2008-03-31 00:01:36 10.11.46.90 - 10.11.1.9 80 GET /acip/shownewsh.asp - 200 Mozilla/4.0+(compatible;+MSIE+6.0;+Windows+NT+5.1;+SV1)
2008-03-31 00:01:38 10.11.44.64 - 10.11.1.9 80 GET /eis/Report/Executive+Information+System - 404 Mozilla/4.0+(compatible;+MSIE+6.0;+Windows+NT+5.0)

其实就是IIS生成的一些log而已。

他还提出了额外的要求:最好在运行程序的机器上不需要额外安装什么runtime,于是排除掉了Java和C#,以及Perl、Ruby等语言;虽然Java可以GCJ到native,Ruby也可以通过rubyscript2exe来生成带有Ruby解释器的独立exe文件,但它们生成出来的exe都太大了,也排除;C和C++都不够方便,我不太想在这种小程序上用。所以最后我选择了用D来写这个小程序。

===========================================================================

于是问题可以分解为几个小问题,主要是:
1、从配置文件读入配置;
2、拼接出新的log:获取当前时间并格式化到合适的格式上,然后与源log拼接起来;
3、按固定的时间间隔重复执行写出log的动作。

---------------------------------------------------------------------------

第一个问题很好办。我的解决办法是把整个配置文件读进来,分解成行,然后对每行做匹配,把配置文件里的key-value对保存到D语言提供的关联数组里。
char[char[]] config; // 配置参数

这样声明了一个char[] => char[]的关联数组。注意到D里char[]就是字符串了。

在对行做匹配时,我用了三个正则表达式:
RegExp commentPattern = RegExp(r"^//.*");
RegExp sectionPattern = RegExp(r"^\[[^\]]+\]");
RegExp keyValuePattern = RegExp(r"(.+)=(.+)");

第一个正则表达式用于匹配行首为"//"的行。如果匹配则忽略掉该行。这其实是偷懒了——我没有去匹配在行尾的行注释,因为如果配置文件里出现引号包围的字符串,而字符串的正常内容含有"//"的话,直接用r"//.*$"会把不应该认为是注释的东西也包含进去……这里需要更多的处理,但是我懒得做了。直接规定用户必须把注释符号写在行首就是。

第二个正则表达式用于匹配配置文件里的段标记。如果匹配则同注释一样,忽略掉该行。

第三个正则表达式用于验证配置文件的行是否符合key=value的形式。如果符合,则将key-value对保存到关联数组里。

源log文件也同样,整个文件读进来,然后分解为行,保存起来。
char[][] source; // 源log的数据


---------------------------------------------------------------------------

第二个问题在D的标准库Phobos的支持下也很好办。特别是当D的字符串是基于char[],也就是说是一个动态数组,用起来很方便。
先要得到当前的系统时间。std.date包里有足够的函数来解决这问题。用std.date.getUTCtime()获取当前时间后,用XXXFromTime()的几个函数把时间转换为字符串,然后用std.string.format()以指定的格式将它们拼接起来。
得到当前时间的字符串之后,随机从源log里选一行出来,把当前时间与去除掉时间的源log拼起来就行。

---------------------------------------------------------------------------

第三个问题比较讨厌。要按照固定的时间间隔来做些事情的话,Java、C#高级语言和JavaScript等脚本语言都提供了直接能用的timer机制,但C/C++的层次上则没有直接能用的timer,D的Phobos也没有。换言之我们要自己实现定时器。有两种思路:
1、busy wait
2、sleep and wait

busy wait就是例如while(true)然后在里面检查时间间隔是不是大于或等于规定值,满足条件的时候执行动作
sleep and wait是在每次执行完动作之后让自己(一个线程)休眠一定时间,等“醒来”的时候再执行一次动作,再休眠,如此循环。

很明显busy wait是很糟糕的选择——它不停的循环,什么事都不做却占着CPU。但是这种做法在C/C++里却很常见。或许大家都很无奈吧 = =
刚才经过隔壁宿舍的的时候看到了一本叫做《C游戏编程从入门到精通》的书,顺手翻了翻,读到了它(还是说是Andre LaMothe?)提供的控制时间延迟的函数:
void Delay(int clicks)
{
    unsigned int far *clock = (unsigned int far *)0x0000046CL;
    unsigned int now;
    now = *clock;
    while (abs(*clock - now) < clicks) { }
}

噢天哪。居然用上了内存到寄存器的地址映射——也就是说直接从寄存器读了当前的clock tick。这比用系统API更“糟糕”了,不采用。这本书居然是在2000年之后才出的,好神奇啊 =v=
(“糟糕”不是说这代码不好。事实上总是得有这样的代码存在于某处,一般是在库里,像是说操作系统的API会有这样的函数的实现。不过某个寄存器到底映射到的地址对平台的依赖性太大了,我们最好不要自己的*应用程序*里直接用……)

sleep and wait则需要库的支持。在C/C++/D里,没有办法不依赖于平台来做这件事。不过依赖就依赖吧诶,总比busy wait好。在D的官网论坛上有人写了一个还不错的实现:http://www.digitalmars.com/d/archives/digitalmars/D/learn/Implementing_a_timer_using_threads_6170.html
下面的代码里就使用了那帖里的Timer。

===========================================================================

说起来,我没写如何让这程序退出的逻辑……要退出就要强制结束进程了 =v=
另外我没有检查目标log的路径是否存在,如果路径上的目录不存在的话程序也会出错。这个得递归的调用mkdir才好解决,懒得做……
用来编译的环境是DMD D 1.028。
好吧,其它也没什么了,完整的程序如下:

loggen.d
import std.conv;
import std.date;
import std.file;
import std.path;
// import std.random; // for rand()
import std.regexp;
import std.stdio;
import std.string;

import std.c.stdlib;
import std.c.time;

static const char[] CONFIG_FILENAME = "./RT_Config.inc";
static const char[] LOG_SOURCE_FILENAME = "./record.log";
static const int FOREVER = -1;

char[][char[]] config; // configuration data
int timeInterval;      // time interval
char[] logFileName;    // destination log file's name
char[][] source;       // source log contents

// Load the configuration data,
// and set time interval/destination filename
bool loadConfig() {
    char[] file = cast(char[])read(CONFIG_FILENAME);
    if(file == null) return false;
    char[][] lines = splitlines(file);

    RegExp commentPattern = RegExp(r"^//.*");
    RegExp sectionPattern = RegExp(r"^\[[^\]]+\]");
    RegExp keyValuePattern = RegExp(r"(.+)=(.+)");
    foreach (char[] line; lines) {
        if (commentPattern.test(line)) continue; // skip comments
        if (sectionPattern.test(line)) continue; // skip section tags
        if (keyValuePattern.test(line)) {
            config[toupper(keyValuePattern.match(1))] = keyValuePattern.match(2);
        }
    }

    timeInterval = toInt(config["TIME_INTERVAL"]);
    logFileName = config["FILENAME"];

    return true;
}

// Load the source log contents
bool loadSource() {
    char[] file = cast(char[])read(LOG_SOURCE_FILENAME);
    if(file == null) return false;
    source = splitlines(file);

    return true;
}

// get a random number in the range [min, max)
// min and max should be a positive integer
int random(int min, int max) {
    int rand = std.c.stdlib.random(20);
    while (rand < 5) rand = std.c.stdlib.random(20);
    return rand;
}

// get a string representation of the current system time
// in the format "YYYY-MM-DD hh:mm:ss"
char[] getCurrentTimeString() {
    d_time lNow = std.date.getUTCtime();
    return std.string.format("%04d-%02d-%02d "
             ~ std.date.toTimeString(lNow)[0..$-9],
               YearFromTime(lNow),
               MonthFromTime(lNow),
               DateFromTime(lNow));
}

// generate a line of dummy log
char[] getLogLine() {
    int rand = std.c.stdlib.random(source.length);
    return getCurrentTimeString() ~ source[rand][19..$];
}

// exit the program abnormally
void abort(char[] message) {
    writefln("Error: " ~ message);
    exit(1);
}

/*
// execute an action repeatly with specified time interval
// busy wait version
void repeat(void delegate() action, int interval, int limit) {
    if (action == null) return;
    if (interval < 0) return;

    time_t old = time(null);
    while (limit != 0) {
        time_t now = time(null);
        if (now >= old + interval) {
            old = now;
            action();
            --limit;
        }
    }
}
*/

// execute an action repeatly with specified time interval
// sleep-and-wait version
// TODO: fix the limit param...
void repeat(void delegate() action, int interval, int limit) {
    Timer timer = new Timer(interval, action, (limit < 0));
    timer.start();
    timer.wait();
}

// program entry point
void main(char[][] args) {
    // load configuration
    if (!loadConfig()) abort("invalid configuration file.");

    // load source log
    if (!loadSource()) abort("invalid source log file.");

    // append a few lines of log first
    int r = random(5, 20);
    // append generated log to destination
    for (int i = 0; i < r; ++i) {
        if (exists(logFileName) && isfile(logFileName)) {
            append(logFileName, newline ~ getLogLine());
        } else { // destinatin log file doesn't exist, create one
            // TODO: check and create directories on the path
            // write log's header comment
            write(logFileName,
                  "#Software: Microsoft Internet Information Services 5.0" ~ newline
                ~ "#Version: 1.0" ~ newline
                ~ "#Date: 2008-03-31 00:00:19" ~ newline
                ~ "#Fields: date time c-ip cs-username s-ip s-port cs-method"
                ~ " cs-uri-stem cs-uri-query sc-status cs(User-Agent)" ~ newline);
            // write first line of log content
            append(logFileName, getLogLine());
        }
    }

    // append new log to destination, in specified time interval
    repeat(delegate() {
        // get a random number in the range [5, 20)
        r = random(5, 20);
        // append generated log to destination
        for (int i = 0; i < r; ++i) {
            append(logFileName, newline ~ getLogLine());
        }
    }, timeInterval, FOREVER);
}

//===============================================================
// Timer, written by Dennis Kempin
//===============================================================

import std.thread;

version(Windows) {
    import std.c.windows.windows;
}

version(linux) {
    import std.c.linux.linux;
}

class Timer: Thread {
    private void delegate() action;
    private int waitTime;
    private bit autoRestart;

    this(int waitTime, void delegate() action, bit autoRestart=false) {
        this.action = action;
        this.waitTime = waitTime;
        this.autoRestart = autoRestart;
    }

    protected this(int waitTime, bit autoRestart=false) {
        this.waitTime = waitTime;
        this.autoRestart = autoRestart;
    }

    override int run() {
        sleep(waitTime);
        execute();
        while(autoRestart)
        {
            sleep(waitTime);
            execute();
        }
        return 0;
    }

    void execute() {
        action();
    }

    private void sleep(int time) {
        version(Windows) {
            Sleep(time*1000);
        }

        version(linux) {
            usleep(time*1000);
        }
    }
}
分享到:
评论

相关推荐

    java 生成批量log日志

    利用java代码实现 生成批量log日志文件

    Log4cpp 在程序中生成日志文件

    Log4cpp 是一个流行的开源日志库,尤其适用于 C++ 开发者,它提供了丰富的功能,使开发者能够方便地在程序中生成和管理日志文件。本文将详细介绍如何在 Windows 和 Linux 系统中使用 Log4cpp 库来实现日志功能。 **...

    NSIS安装过程生成log,卸载过程调用log卸载文件

    这将使安装程序在目标目录下生成一个名为“install.log”的日志文件。 在卸载过程中调用log来卸载文件是一种确保完整性和一致性的方法。当卸载程序读取安装时生成的日志,它可以准确知道哪些文件是在安装过程中被...

    log4cpp(添加按日期生成日志类)

    "Log4cpp(添加按日期生成日志类)" 提示我们这是一个关于Log4cpp的项目,而且已经进行了扩展,增加了按照日期生成日志文件的功能。Log4cpp是C++的一个日志记录库,它借鉴了Java中的log4j框架,提供了灵活的日志管理...

    log4net 配置 根据日期生成 文件 根据 分类 (INFO、ERROR) 生成文件

    log4net是一个开放源代码的日志框架,用于.NET平台的应用程序。它基于Apache Logging Services的log4j项目,并进行了扩展和优化,使其更适应.NET环境。log4net提供了一种灵活的日志记录机制,可以将日志信息输出到...

    名字生成log脚本exe

    名字生成log脚本exe

    自动ping并生成log报告

    自动ping并生成log报告。 默认是ping天津电信DNS,可右键单击后编辑,把219.150.32.132改成你需要去ping的IP即可。 注:尽量不要改文件名和其他参数。

    tomcat下用Log4j 按文件大小,生成catalina.out日志文件

    在Java Web开发中,Tomcat是一个广泛使用的应用服务器,它负责运行我们的Servlet和JSP应用程序。日志系统是任何应用程序的重要组成部分,它帮助开发者在调试、监控和问题排查时收集必要的信息。Log4j是Apache的一个...

    log4j生成文件及文件夹

    本文将深入探讨如何利用Log4j生成动态的日志文件名以及动态创建文件夹,帮助开发者更好地管理和分析应用程序的运行状态。 Log4j是一款功能强大的开源日志组件,它允许程序员灵活地控制日志信息的输出级别,如DEBUG...

    log4j生成带时间戳的日志文件(包括extras包以及简单demo)

    本文将深入探讨如何使用Log4j生成带有时间戳的日志文件,同时还会涉及到extras包的使用以及一个简单的示例。 1. **Log4j基本概念** - **日志级别**:Log4j支持多种日志级别,包括DEBUG、INFO、WARN、ERROR和FATAL...

    log4j测试程序

    "Log4j测试程序" 这个标题表明了这是一个关于Log4j的日志测试项目,Log4j是Java编程语言中的一个开源日志记录框架,广泛用于各种应用程序,它允许开发者灵活地控制日志信息的输出。 **描述解析:** "log4j入门程序...

    利用伪随机数理论生成均匀分布的高斯白噪声(VC++程序)

    总的来说,通过理解伪随机数生成和高斯白噪声的特性,我们可以编写程序在VC++环境中生成所需的噪声,并通过可视化工具观察其时域表现。这对于理解和模拟随机过程至关重要,也常用于各种科学和工程计算。

    java log 日志示例程序

    下面是一个简单的`LogDemo`程序示例: ```java import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; public class LogDemo { private static final Log log = LogFactory.get...

    Tomcat下使用Log4j接管生成日志文件

    Log4j是一个广泛使用的开源日志框架,提供灵活的日志配置和多种级别的日志输出。本文将详细介绍如何在Tomcat服务器环境下使用Log4j接管生成日志文件。 首先,了解Log4j的核心组件: 1. **Logger**:负责生成日志...

    log函数快速计算c语言程序

    在C语言编程中,log函数通常用于执行对数计算,它是数学中的一个重要概念,尤其在科学计算、工程问题以及数据分析等领域广泛应用。C语言标准库提供了数学函数,包括log函数,来帮助程序员处理这类计算。本篇文章将...

    log4j按照不同appender生成日志例子

    在这个配置中,我们定义了两个FileAppender,一个用于接口A(A.log),另一个用于接口B(B.log)。通过设置`log4j.logger`属性,我们可以指定这些接口的日志输出应该被哪个Appender捕获。例如,所有在`...

    Wpf log4net 日志

    这段配置中,我们定义了一个名为"RollingFileAppender"的滚动文件追加器,设置日志文件存储路径为"logs"目录下的"log.txt",并按照日期(年-月-日)滚动生成新的日志文件。`datePattern`属性指定了文件名格式,`...

    log4Net测试程序

    在`testLog4Net`这个测试程序中,可能包含了一个简单的控制台应用程序,该程序执行特定的操作并在执行过程中使用log4Net记录日志。测试可能涉及不同的日志级别,确保正确地记录和输出信息。通过运行程序并检查生成...

Global site tag (gtag.js) - Google Analytics