📄 filechannel.cpp
字号:
//
// FileChannel.cpp
//
// $Id: //poco/Main/Foundation/src/FileChannel.cpp#5 $
//
// Copyright (c) 2004, Guenter Obiltschnig/Applied Informatics.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// 3. Redistributions in any form must be accompanied by information on
// how to obtain complete source code for this software and any
// accompanying software that uses this software. The source code
// must either be included in the distribution or be available for no
// more than the cost of distribution plus a nominal fee, and must be
// freely redistributable under reasonable conditions. For an
// executable file, complete source code means the source code for all
// modules it contains. It does not include source code for modules or
// files that typically accompany the major components of the operating
// system on which the executable file runs.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
#include "Foundation/FileChannel.h"
#include "Foundation/Message.h"
#include "Foundation/File.h"
#include "Foundation/NumberFormatter.h"
#include "Foundation/DateTimeFormatter.h"
#include "Foundation/Timespan.h"
#include "Foundation/Exception.h"
#include <ctype.h>
#if defined(POCO_OS_FAMILY_WINDOWS)
#include <windows.h>
#elif defined(POCO_OS_FAMILY_VMS)
#include <stdio.h>
#else
#include <fstream>
#endif
Foundation_BEGIN
//
// LogFile
//
// Specific implementations of LogFile are
// provided for Windows, OpenVMS and the rest.
//
#if defined(POCO_OS_FAMILY_WINDOWS)
class LogFile
/// The implementation of LogFile for Windows.
/// The native filesystem APIs are used for
/// total control over locking behavior.
{
public:
LogFile(const std::string& path): _path(path)
{
_hFile = CreateFile(path.c_str(), GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (_hFile == INVALID_HANDLE_VALUE) throw OpenFileException(path);
SetFilePointer(_hFile, 0, 0, FILE_END);
// There seems to be a strange "optimization" in the Windows NTFS
// filesystem that causes it to reuse directory entries of deleted
// files. Example:
// 1. create a file named "test.dat"
// note the file's creation date
// 2. delete the file "test.dat"
// 3. wait a few seconds
// 4. create a file named "test.dat"
// the new file will have the same creation
// date as the old one.
// We work around this bug by taking the file's
// modification date as a reference when the
// file is empty.
if (size() == 0)
_creationDate = File(path).getLastModified();
else
_creationDate = File(path).created();
}
~LogFile()
{
CloseHandle(_hFile);
}
void write(const std::string& text)
{
DWORD bytesWritten;
BOOL res = WriteFile(_hFile, text.data(), (DWORD) text.size(), &bytesWritten, NULL);
if (!res) throw WriteFileException(_path);
res = WriteFile(_hFile, "\r\n", 2, &bytesWritten, NULL);
if (!res) throw WriteFileException(_path);
res = FlushFileBuffers(_hFile);
if (!res) throw WriteFileException(_path);
}
UInt64 size()
{
LARGE_INTEGER li;
li.HighPart = 0;
li.LowPart = SetFilePointer(_hFile, 0, &li.HighPart, FILE_CURRENT);
return li.QuadPart;
}
Timestamp creationDate() const
{
return _creationDate;
}
const std::string& path() const
{
return _path;
}
private:
std::string _path;
HANDLE _hFile;
Timestamp _creationDate;
};
#elif defined(POCO_OS_FAMILY_VMS)
class LogFile
/// The implementation of LogFile for OpenVMS.
/// The C Standard Library functions for file I/O
/// are used with OpenVMS-specific extensions to
/// control sharing and locking behavior.
{
public:
LogFile(const std::string& path): _path(path)
{
_file = fopen(path.c_str(), "a", "ctx=rec,bin", "shr=get", "fop=dfw", "alq=500", "deq=500");
if (!_file) throw OpenFileException(path);
if (size() == 0)
_creationDate = File(path).getLastModified();
else
_creationDate = File(path).created();
}
~LogFile()
{
fclose(_file);
}
void write(const std::string& text)
{
int rc = fputs(text.c_str(), _file);
if (rc == EOF) throw WriteFileException(_path);
rc = fputc('\n', _file);
if (rc == EOF) throw WriteFileException(_path);
rc = fflush(_file);
if (rc == EOF) throw WriteFileException(_path);
}
UInt64 size()
{
return (UInt64) ftell(_file);
}
Timestamp creationDate() const
{
return _creationDate;
}
const std::string& path() const
{
return _path;
}
private:
std::string _path;
FILE* _file;
Timestamp _creationDate;
};
#else
class LogFile
/// The standard implementation of LogFile
/// that uses Standard Library streams.
{
public:
LogFile(const std::string& path): _path(path)
{
_str.open(path.c_str(), std::ios::ate);
if (!_str.good()) throw OpenFileException(path);
if (size() == 0)
_creationDate = File(path).getLastModified();
else
_creationDate = File(path).created();
}
~LogFile()
{
}
void write(const std::string& text)
{
_str << text << std::endl;
if (!_str.good()) throw WriteFileException(_path);
}
UInt64 size()
{
return (UInt64) _str.tellp();
}
Timestamp creationDate() const
{
return _creationDate;
}
const std::string& path() const
{
return _path;
}
private:
std::string _path;
std::ofstream _str;
Timestamp _creationDate;
};
#endif
//
// RotateStrategy
//
class RotateStrategy
/// The RotateStrategy is used to determine when
/// a file must be rotated.
{
public:
virtual bool mustRotate(LogFile* pFile) = 0;
/// Returns true if the given log file must
/// be rotated, false otherwise.
virtual ~RotateStrategy()
{
}
};
class RotateByIntervalStrategy: public RotateStrategy
/// The file is rotated when the log file
/// exceeds a given age.
{
public:
RotateByIntervalStrategy(const Timespan& span): _span(span)
{
if (span <= 0) throw InvalidArgumentException("time span must be greater than zero");
}
~RotateByIntervalStrategy()
{
}
bool mustRotate(LogFile* pFile)
{
Timestamp now;
return _span <= now - pFile->creationDate();
}
private:
Timespan _span;
};
class RotateBySizeStrategy: public RotateStrategy
/// The file is rotated when the log file
/// exceeds a given size.
{
public:
RotateBySizeStrategy(UInt64 size): _size(size)
{
if (size == 0) throw InvalidArgumentException("size must be greater than zero");
}
~RotateBySizeStrategy()
{
}
bool mustRotate(LogFile* pFile)
{
return pFile->size() >= _size;
}
private:
UInt64 _size;
};
//
// ArchiveStrategy
//
class ArchiveStrategy
/// The ArchiveStrategy is used to rename
/// a rotated log file for archiving.
{
public:
virtual LogFile* archive(LogFile* pFile) = 0;
/// Renames the given log file for archiving
/// and creates and returns a new log file.
/// The given LogFile object is deleted.
virtual ~ArchiveStrategy()
{
}
};
class ArchiveByNumberStrategy: public ArchiveStrategy
/// A monotonic increasing number is appended to the
/// log file name. The most recent archived file
/// always has the number zero.
{
public:
ArchiveByNumberStrategy()
{
}
~ArchiveByNumberStrategy()
{
}
LogFile* archive(LogFile* pFile)
{
std::string basePath = pFile->path();
delete pFile;
int n = -1;
std::string path;
do
{
path = basePath;
path.append(".");
path.append(NumberFormatter::format(++n));
}
while (File(path).exists());
while (n >= 0)
{
std::string oldPath = basePath;
if (n > 0)
{
oldPath.append(".");
oldPath.append(NumberFormatter::format(n - 1));
}
std::string newPath = basePath;
newPath.append(".");
newPath.append(NumberFormatter::format(n));
File f(oldPath);
f.renameTo(newPath);
--n;
}
return new LogFile(basePath);
}
};
class ArchiveByTimestampStrategy: public ArchiveStrategy
/// A timestamp (YYYYMMDDhhmmss) is appended to archived
/// log files.
{
public:
ArchiveByTimestampStrategy()
{
}
~ArchiveByTimestampStrategy()
{
}
LogFile* archive(LogFile* pFile)
{
std::string path = pFile->path();
delete pFile;
std::string archPath = path;
archPath.append(".");
archPath.append(DateTimeFormatter::format(Timestamp(), "%Y%m%d%H%M%S"));
File f(path);
f.renameTo(archPath);
return new LogFile(path);
}
};
//
// FileChannel
//
const std::string FileChannel::PROP_PATH = "path";
const std::string FileChannel::PROP_ROTATION = "rotation";
const std::string FileChannel::PROP_ARCHIVE = "archive";
FileChannel::FileChannel():
_pFile(0),
_pRotateStrategy(0),
_pArchiveStrategy(new ArchiveByNumberStrategy)
{
}
FileChannel::FileChannel(const std::string& path):
_path(path),
_pFile(0),
_pRotateStrategy(0),
_pArchiveStrategy(new ArchiveByNumberStrategy)
{
}
FileChannel::~FileChannel()
{
close();
delete _pRotateStrategy;
delete _pArchiveStrategy;
}
void FileChannel::open()
{
FastMutex::ScopedLock lock(_mutex);
if (!_pFile)
{
_pFile = new LogFile(_path);
}
}
void FileChannel::close()
{
FastMutex::ScopedLock lock(_mutex);
delete _pFile;
_pFile = 0;
}
void FileChannel::log(const Message& msg)
{
poco_check_ptr (_pFile);
FastMutex::ScopedLock lock(_mutex);
if (_pRotateStrategy && _pArchiveStrategy && _pRotateStrategy->mustRotate(_pFile))
{
try
{
_pFile = _pArchiveStrategy->archive(_pFile);
}
catch (...)
{
_pFile = new LogFile(_path);
}
}
_pFile->write(msg.getText());
}
void FileChannel::setProperty(const std::string& name, const std::string& value)
{
FastMutex::ScopedLock lock(_mutex);
if (name == PROP_PATH)
_path = value;
else if (name == PROP_ROTATION)
setRotation(value);
else if (name == PROP_ARCHIVE)
setArchive(value);
else
Channel::setProperty(name, value);
}
std::string FileChannel::getProperty(const std::string& name) const
{
if (name == PROP_PATH)
return _path;
else if (name == PROP_ROTATION)
return _rotation;
else if (name == PROP_ARCHIVE)
return _archive;
else
return Channel::getProperty(name);
}
Timestamp FileChannel::creationDate() const
{
if (_pFile)
return _pFile->creationDate();
else
return 0;
}
UInt64 FileChannel::size() const
{
if (_pFile)
return _pFile->size();
else
return 0;
}
const std::string& FileChannel::path() const
{
return _path;
}
void FileChannel::setRotation(const std::string& rotation)
{
std::string::const_iterator it = rotation.begin();
std::string::const_iterator end = rotation.end();
int n = 0;
while (it != end && isspace(*it)) ++it;
while (it != end && isdigit(*it)) { n *= 10; n += *it++ - '0'; }
while (it != end && isspace(*it)) ++it;
std::string unit;
while (it != end && isalpha(*it)) unit += *it++;
RotateStrategy* pStrategy = 0;
if (unit == "daily")
pStrategy = new RotateByIntervalStrategy(Timespan(1*Timespan::DAYS));
else if (unit == "weekly")
pStrategy = new RotateByIntervalStrategy(Timespan(7*Timespan::DAYS));
else if (unit == "monthly")
pStrategy = new RotateByIntervalStrategy(Timespan(30*Timespan::DAYS));
else if (unit == "seconds") // for testing only
pStrategy = new RotateByIntervalStrategy(Timespan(n*Timespan::SECONDS));
else if (unit == "hours")
pStrategy = new RotateByIntervalStrategy(Timespan(n*Timespan::HOURS));
else if (unit == "days")
pStrategy = new RotateByIntervalStrategy(Timespan(n*Timespan::DAYS));
else if (unit == "weeks")
pStrategy = new RotateByIntervalStrategy(Timespan(n*7*Timespan::DAYS));
else if (unit == "months")
pStrategy = new RotateByIntervalStrategy(Timespan(n*30*Timespan::DAYS));
else if (unit == "K")
pStrategy = new RotateBySizeStrategy(n*1024);
else if (unit == "M")
pStrategy = new RotateBySizeStrategy(n*1024*1024);
else if (unit.empty())
pStrategy = new RotateBySizeStrategy(n);
else if (unit != "never")
throw InvalidArgumentException("rotation", rotation);
delete _pRotateStrategy;
_pRotateStrategy = pStrategy;
_rotation = rotation;
}
void FileChannel::setArchive(const std::string& archive)
{
ArchiveStrategy* pStrategy = 0;
if (archive == "number")
pStrategy = new ArchiveByNumberStrategy;
else if (archive == "timestamp")
pStrategy = new ArchiveByTimestampStrategy;
else
throw InvalidArgumentException("archive", archive);
delete _pArchiveStrategy;
_pArchiveStrategy = pStrategy;
_archive = archive;
}
Foundation_END
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -