⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 filechannel.cpp

📁 This software aims to create an applet and panel tools to manage a wireless interface card, such as
💻 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 + -