text: #include "log_file.h"
  
  #include <errno.h>
  #include <fcntl.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  #include <sys/mman.h>
  #include <sys/socket.h>
  #include <sys/types.h>
  #include <unistd.h>
  
  namespace hardcode {
  namespace naruto {
  
  namespace {
  class MMapFileWriter : public FileWriter {
  public:
    MMapFileWriter(const std::string &basename, uint32_t mem_size) {
      mem_size_ = mem_size;
      writed_ = 0;
      if (fd_ >= 0) {
        close(fd_);
      }
      fd_ = open(basename.c_str(), O_RDWR | O_CREAT,
                 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
      if (fd_ < 0) {
        fprintf(stderr, "open new file failed,errno=%d", errno);
      } else {
        int n = ftruncate(fd_, mem_size);
        (void)n;
        buffer_ = (char *)mmap(NULL, mem_size_, PROT_READ | PROT_WRITE,
                               MAP_SHARED, fd_, 0);
        if (buffer_ == MAP_FAILED) {
          fprintf(stderr, "mmap file failed,errno=%d", errno);
        }
      }
    }
    ~MMapFileWriter() {
      if (fd_ >= 0) {
        close(fd_);
        fd_ = -1;
      }
      if (buffer_ != MAP_FAILED) {
        munmap(buffer_, mem_size_);
      }
    }
    void append(const char *msg, int32_t len) {
      if (len > mem_size_ - writed_) {
        fprintf(stderr, "mmap memory overflow ,errno=%d", errno);
        return;
      }
      memcpy(buffer_ + writed_, msg, len);
      writed_ += len;
    }
    void flush() {
      if (buffer_ != MAP_FAILED) {
        msync(buffer_, mem_size_, MS_ASYNC);
      }
    }
    uint32_t writtenBytes() const { return writed_; }
  
  private:
    int fd_;
    char *buffer_;
    int32_t mem_size_;
    int32_t writed_;
  };
  
  class AppendFileWriter : public FileWriter {
  public:
    AppendFileWriter(const std::string &filename)
        : fp_(::fopen(filename.c_str(), "ae")) {
      ::setbuffer(fp_, buffer_, sizeof buffer_);
    }
    ~AppendFileWriter() {
      if (fp_) {
        ::fclose(fp_);
      }
    }
    void append(const char *msg, int32_t len) {
      size_t n = ::fwrite_unlocked(msg, 1, len, fp_);
      size_t remain = len - n;
      while (remain > 0) {
        size_t x = ::fwrite_unlocked(msg + n, 1, remain, fp_);
        if (x == 0) {
          int err = ferror(fp_);
          if (err) {
            fprintf(stderr, "AppendFile::append() failed %s\n", strerror(err));
          }
          break;
        }
        n += x;
        remain = len - n; // remain -= x
      }
  
      writen_ += len;
    }
    void flush() { ::fflush(fp_); }
    uint32_t writtenBytes() const { return writen_; }
  
  private:
    FILE *fp_;
    char buffer_[64 * 1024];
    uint32_t writen_ = 0;
  };
  } // namespace
  
  namespace {
  
  std::string getHostName() {
    // HOST_NAME_MAX 64
    // _POSIX_HOST_NAME_MAX 255
    char buf[256];
    if (::gethostname(buf, sizeof buf) == 0) {
      buf[sizeof(buf) - 1] = '\0';
      return buf;
    } else {
      return "unknownhost";
    }
  }
  std::string getLogFileName(const std::string &basename, time_t *now) {
    std::string filename;
    filename.reserve(basename.size() + 64);
    filename = basename;
  
    char timebuf[32];
    struct tm tm;
    *now = time(NULL);
    gmtime_r(now, &tm); // FIXME: localtime_r ?
    strftime(timebuf, sizeof timebuf, ".%Y%m%d-%H%M%S.", &tm);
    filename += timebuf;
  
    filename += getHostName();
  
    char pidbuf[32];
    snprintf(pidbuf, sizeof pidbuf, ".%d", getpid());
    filename += pidbuf;
  
    filename += ".log";
  
    return filename;
  }
  } // namespace
  
  LogFile::LogFile(const std::string &basename, int32_t roll_size,
                   int32_t flush_interval, int32_t check_interval,
                   FileWriterType file_writer_type)
      : basename_(basename), roll_size_(roll_size),
        flush_interval_(flush_interval), check_freq_count_(check_interval),
        count_(0), start_of_period_(0), last_roll_(0), last_flush_(0) {
    time_t now = 0;
    std::string filename = getLogFileName(basename_, &now);
    if (file_writer_type == FileWriterType::MMAPFILE) {
      file_ = std::make_shared<MMapFileWriter>(filename, roll_size_);
    } else {
      file_ = std::make_shared<AppendFileWriter>(filename);
    }
    file_writer_type_ = file_writer_type;
    rollFile();
  }
  
  LogFile::~LogFile() = default;
  
  void LogFile::append(const char *msg, int32_t len) {
    file_->append(msg, len);
    if (file_->writtenBytes() > roll_size_) {
      rollFile();
    } else {
      ++count_;
      //隔多久需要检查一下
      if (count_ >= check_freq_count_) {
        count_ = 0;
        time_t now = ::time(NULL);
        time_t this_period = now / kRollPerSeconds * kRollPerSeconds;
        if (this_period != start_of_period_) {
          rollFile();
        } else if (now - last_flush_ > flush_interval_) {
          last_flush_ = now;
          file_->flush();
        }
      }
    }
  }
  
  void LogFile::flush() { file_->flush(); }
  
  bool LogFile::rollFile() {
    time_t now = 0;
    std::string filename = getLogFileName(basename_, &now);
    time_t start = now / kRollPerSeconds * kRollPerSeconds;
  
    if (now > last_roll_) {
      last_roll_ = now;
      last_flush_ = now;
      start_of_period_ = start;
      if (file_writer_type_ == FileWriterType::MMAPFILE) {
        file_.reset(new MMapFileWriter(filename, roll_size_));
      } else {
        file_.reset(new AppendFileWriter(filename));
      }
    }
    return true;
  }
  
  } // namespace naruto
  
  } // namespace hardcode
  