简体   繁体   English

记录对 Java servlet 的访问

[英]Logging access to Java servlet

I'm currently developing a Java Web application using Servlets.我目前正在使用 Servlet 开发 Java Web 应用程序。 What I need to do is log to a file every access made to the website.我需要做的是每次访问该网站时都将其记录到一个文件中。 To do that, I used Filters.为此,我使用了过滤器。 So far, I've made it to the point where I can print everything to the console.到目前为止,我已经达到了可以将所有内容打印到控制台的程度。

What I now need to do is store that into a file with a maximum of 10.000 entries up to 30 days old (if the maximum entries is achieved, the oldest ones are replaced when a new one is written).我现在需要做的是将它存储到一个最多 10.000 个条目的文件中,最长可达 30 天(如果达到最大条目数,则在写入新条目时替换最旧的条目)。

How can I do that?我怎样才能做到这一点?

PS: I cannot use a database for this assignment PS:我不能在这个作业中使用数据库

Edit: I am not using a web framework.编辑:我没有使用网络框架。 I can use logging frameworks.我可以使用日志框架。

So, this question actually prompted me to investigate whether any of the popular logging frameworks can actually do the task as requested.所以,这个问题实际上促使我调查是否有任何流行的日志框架实际上可以按要求完成任务。

While most do rolling logs based on file size and date/time, none of them had an easy way to do a rolling log based on entries in the log file.虽然大多数都根据文件大小和日期/时间来滚动日志,但他们都没有一种简单的方法可以根据日志文件中的条目来滚动日志。 Also, existing logging frameworks typically store each day (and sometimes smaller units of time) in their own separate file, making for efficient cleanup based on date/time.此外,现有的日志框架通常将每一天(有时是更小的时间单位)存储在它们自己的单独文件中,从而根据日期/时间进行有效的清理。

With a requirement for a maximum number of lines inside a single file, this necessitates reading the entire file into memory (very inefficient!).由于需要单个文件中的最大行数,这需要将整个文件读入内存(非常低效!)。 When everything, past and present is being written to a single file, removing older entries requires parsing each line for the date/time that entry was written (also, inefficient!).当过去和现在的所有内容都被写入单个文件时,删除旧条目需要解析每行的写入日期/时间(同样,效率低下!)。

Below is a simple program to demonstrate that this can be done, but there are some serious problems with this approach:下面是一个简单的程序来证明可以做到这一点,但是这种方法存在一些严重的问题:

  • Not thread safe (if two threads try to read/write an entry simultaneously, one will be clobbered and the message will be skipped)不是线程安全的(如果两个线程尝试同时读/写一个条目,一个将被破坏并且消息将被跳过)
  • Slurping is bad (ten thousand entries is a lot: can the server slurp all that into memory?)啜饮很糟糕(一万个条目很多:服务器可以将所有这些都啜饮到内存中吗?)

This is probably suitable for a toy project, a demonstration, or a school assignment.这可能适用于玩具项目、演示或学校作业。

This is NOT suitable for production applications, or really anything on the web that more than one person is going to use at a time.这不适合生产应用程序,或者网络上任何一个多人同时使用的东西。


In short, if you try to use a handi-craft program that you found on the internet for a mission-critical application that other people depend on, you are going to get exactly what you deserve.简而言之,如果您尝试将在 Internet 上找到的手工程序用于其他人依赖的关键任务应用程序,那么您将得到您应得的。


public static void main(final String[] args) throws Exception
{
    final File logFile = new File("C:/", "EverythingInOneBigGiant.log");
    final int maxDays = 30;
    final int maxEntries = 10000;

    while (true)
    {
        // Just log the current time for this example, also makes parsing real simple
        final String msg = Instant.now().toString();
        slurpAndParse(logFile, msg, maxDays, maxEntries);

        // Wait a moment, before writing another entry
        Thread.sleep(750);
    }
}

private static void slurpAndParse(final File f, final String msg, final int maxDays, final int maxEntries)
        throws Exception
{
    // Slurp entire file into this buffer (possibly very large!)
    // Could crash your server if you run out of memory
    final StringBuffer sb = new StringBuffer();

    if (f.exists() && f.isFile())
    {
        final LocalDateTime now = LocalDateTime.now();

        final long totalLineCount = Files.lines(Paths.get(f.getAbsolutePath())).count();
        final long startAtLine = (totalLineCount < maxEntries ? 0 : (totalLineCount - maxEntries) + 1);
        long currentLineCount = 0;

        try (final BufferedReader br = new BufferedReader(new FileReader(f)))
        {
            String line;
            while (null != (line = br.readLine()))
            {
                // Ignore all lines before the start counter
                if (currentLineCount < startAtLine)
                {
                    ++currentLineCount;
                    continue;
                }

                // Parsing log data... while writing to the same log... ugh... how hideous
                final LocalDateTime lineDate = LocalDateTime.parse(line, DateTimeFormatter.ISO_ZONED_DATE_TIME);
                final Duration timeBetween = Duration.between(lineDate, now);
                // ... or maybe just use Math.abs() here? I defer to the date/time buffs
                final long dayDiff = (timeBetween.isNegative() ? timeBetween.negated() : timeBetween).toDays();

                // Only accept lines less than the max age in days
                if (dayDiff <= maxDays)
                {
                    sb.append(line);
                    sb.append(System.lineSeparator());
                }
            }
        }
    }

    System.out.println(msg);

    // Add the new log entry
    sb.append(msg);
    sb.append(System.lineSeparator());

    writeLog(f, sb.toString());
}

private static void writeLog(final File f, final String content) throws IOException
{
    try (final Writer out = new FileWriter(f))
    {
        out.write(content);
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM