extern crate chrono;
use self::chrono::prelude::*;
extern crate time;
extern crate parking_lot;
use self::parking_lot::Mutex;
use std::sync::Arc;
use std::fmt;
use std::fmt::Write;
use formatters::Formatter;
use levels::LogLevel;
use record::Record;
const PREALLOC: usize = 128;
#[doc(hidden)]
#[derive(Clone)]
pub struct RecordMeta {
pub level: LogLevel,
pub module: &'static str,
pub file: &'static str,
pub line: u32,
}
struct RecordLazyMetaInner {
msg: Option<Arc<String>>,
formatted: Option<Arc<String>>,
ts_utc: Option<Arc<DateTime<UTC>>>,
}
#[inline(always)]
fn format(args: fmt::Arguments) -> String {
let mut mstr = String::with_capacity(PREALLOC);
let _ = mstr.write_fmt(args);
mstr
}
impl RecordLazyMetaInner {
#[inline(always)]
fn new() -> Self {
RecordLazyMetaInner {
msg: None,
formatted: None,
ts_utc: None,
}
}
fn mk_msg<'a>(&mut self, args: fmt::Arguments<'a>) {
if self.msg.is_none() {
self.msg = Some(Arc::new(format(args)));
}
}
fn mk_msg_str(&mut self, msg: &String) {
if self.msg.is_none() {
self.msg = Some(Arc::new(msg.clone()));
}
}
fn mk_ts_utc(&mut self, ts: &time::Timespec) {
if self.ts_utc.is_none() {
let naive = chrono::NaiveDateTime::from_timestamp(ts.sec, ts.nsec as u32);
self.ts_utc = Some(Arc::new(chrono::DateTime::from_utc(naive, chrono::UTC)));
}
}
}
struct RecordLazyMeta {
irecord: Mutex<RecordLazyMetaInner>,
formatter: Arc<Formatter>,
}
impl RecordLazyMeta {
#[inline(always)]
fn new(formatter: Arc<Formatter>) -> Self {
RecordLazyMeta {
irecord: Mutex::new(RecordLazyMetaInner::new()),
formatter: formatter,
}
}
fn msg<'a>(&self, args: fmt::Arguments<'a>) -> Arc<String> {
let mut irecord = self.irecord.lock();
irecord.mk_msg(args);
let msg = irecord.msg.as_ref().unwrap();
msg.clone()
}
fn msg_str(&self, msg: &String) -> Arc<String> {
let mut irecord = self.irecord.lock();
irecord.mk_msg_str(msg);
let msg = irecord.msg.as_ref().unwrap();
msg.clone()
}
fn formatted(&self, record: &Record) -> Arc<String> {
{
let irecord = self.irecord.lock();
let format = irecord.formatted.is_none();
drop(irecord);
if format {
let formatted = Arc::new((self.formatter)(record));
let mut irecord = self.irecord.lock();
irecord.formatted = Some(formatted.clone());
return formatted;
}
}
let irecord = self.irecord.lock();
let formatted = irecord.formatted.as_ref().unwrap();
formatted.clone()
}
fn ts_utc(&self, ts: &time::Timespec) -> Arc<DateTime<UTC>> {
let mut irecord = self.irecord.lock();
irecord.mk_ts_utc(ts);
let ts_utc = irecord.ts_utc.as_ref().unwrap();
ts_utc.clone()
}
}
#[doc(hidden)]
pub(crate) struct SyncRecord<'a> {
irecord: &'static RecordMeta,
args: fmt::Arguments<'a>,
precord: RecordLazyMeta,
ts: time::Timespec,
}
impl<'a> SyncRecord<'a> {
#[doc(hidden)]
#[inline(always)]
pub(crate) fn new(record: &'static RecordMeta,
ts: time::Timespec,
args: fmt::Arguments<'a>,
formatter: Arc<Formatter>) -> Self {
SyncRecord {
irecord: record,
ts: ts,
args: args,
precord: RecordLazyMeta::new(formatter),
}
}
}
impl<'a> Record for SyncRecord<'a> {
#[inline(always)]
fn level(&self) -> LogLevel {
self.irecord.level
}
#[inline(always)]
fn module(&self) -> &'static str {
self.irecord.module
}
#[inline(always)]
fn file(&self) -> &'static str {
self.irecord.file
}
#[inline(always)]
fn line(&self) -> u32 {
self.irecord.line
}
#[inline(always)]
fn ts(&self) -> time::Timespec {
self.ts
}
fn msg(&self) -> Arc<String> {
self.precord.msg(self.args)
}
fn formatted(&self) -> Arc<String> {
self.precord.formatted(self)
}
fn ts_utc(&self) -> Arc<DateTime<UTC>> {
self.precord.ts_utc(&self.ts)
}
}
#[doc(hidden)]
pub(crate) struct AsyncRecord {
irecord: &'static RecordMeta,
msg: String,
precord: RecordLazyMeta,
ts: time::Timespec,
}
impl Record for AsyncRecord {
#[inline(always)]
fn level(&self) -> LogLevel {
self.irecord.level
}
#[inline(always)]
fn module(&self) -> &'static str {
self.irecord.module
}
#[inline(always)]
fn file(&self) -> &'static str {
self.irecord.file
}
#[inline(always)]
fn line(&self) -> u32 {
self.irecord.line
}
#[inline(always)]
fn ts(&self) -> time::Timespec {
self.ts
}
fn msg(&self) -> Arc<String> {
self.precord.msg_str(&self.msg)
}
fn formatted(&self) -> Arc<String> {
self.precord.formatted(self)
}
fn ts_utc(&self) -> Arc<DateTime<UTC>> {
self.precord.ts_utc(&self.ts)
}
}
impl<'a> From<SyncRecord<'a>> for AsyncRecord {
#[inline(always)]
fn from(orig: SyncRecord) -> AsyncRecord {
AsyncRecord {
irecord: orig.irecord,
msg: format(orig.args),
precord: orig.precord,
ts: orig.ts,
}
}
}