Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in / Register
Toggle navigation
S
spdlog
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Packages
Packages
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
submodule
spdlog
Commits
e148b939
Commit
e148b939
authored
Oct 19, 2014
by
gabi
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
utc offset support (%z) in pattern formatter
parent
32a8b51d
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
106 additions
and
45 deletions
+106
-45
os.h
include/c11log/details/os.h
+29
-6
pattern_formatter.h
include/c11log/details/pattern_formatter.h
+77
-39
No files found.
include/c11log/details/os.h
View file @
e148b939
...
@@ -33,6 +33,25 @@ inline std::tm localtime()
...
@@ -33,6 +33,25 @@ inline std::tm localtime()
return
localtime
(
now_t
);
return
localtime
(
now_t
);
}
}
inline
std
::
tm
gmtime
(
const
std
::
time_t
&
time_tt
)
{
#ifdef _WIN32
std
::
tm
tm
;
gmtime_s
(
&
tm
,
&
time_tt
);
#else
std
::
tm
tm
;
lgmtime_r
(
&
time_tt
,
&
tm
);
#endif
return
tm
;
}
inline
std
::
tm
gmtime
()
{
std
::
time_t
now_t
=
time
(
0
);
return
gmtime
(
now_t
);
}
inline
bool
operator
==
(
const
std
::
tm
&
tm1
,
const
std
::
tm
&
tm2
)
inline
bool
operator
==
(
const
std
::
tm
&
tm1
,
const
std
::
tm
&
tm2
)
{
{
return
(
tm1
.
tm_sec
==
tm2
.
tm_sec
&&
return
(
tm1
.
tm_sec
==
tm2
.
tm_sec
&&
...
@@ -46,7 +65,7 @@ inline bool operator==(const std::tm& tm1, const std::tm& tm2)
...
@@ -46,7 +65,7 @@ inline bool operator==(const std::tm& tm1, const std::tm& tm2)
inline
bool
operator
!=
(
const
std
::
tm
&
tm1
,
const
std
::
tm
&
tm2
)
inline
bool
operator
!=
(
const
std
::
tm
&
tm1
,
const
std
::
tm
&
tm2
)
{
{
return
!
(
tm1
==
tm2
);
return
!
(
tm1
==
tm2
);
}
}
#ifdef _WIN32
#ifdef _WIN32
...
@@ -84,14 +103,18 @@ inline bool fopen_s(FILE** fp, const std::string& filename, const char* mode)
...
@@ -84,14 +103,18 @@ inline bool fopen_s(FILE** fp, const std::string& filename, const char* mode)
#endif
#endif
}
}
inline
float
tz_offset
()
//Return utc offset in minutes or -1 on failure
inline
int
utc_minutes_offset
(
const
std
::
tm
&
tm
=
localtime
())
{
{
#ifdef _WIN32
#ifdef _WIN32
TIME_ZONE_INFORMATION
tzinfo
;
DYNAMIC_TIME_ZONE_INFORMATION
tzinfo
;
GetTimeZoneInformation
(
&
tzinfo
);
auto
rv
=
GetDynamicTimeZoneInformation
(
&
tzinfo
);
return
tzinfo
.
Bias
/
-
60
.
0
f
;
if
(
!
rv
)
return
-
1
;
return
-
1
*
(
tzinfo
.
Bias
+
tzinfo
.
DaylightBias
);
#else
#else
return
0
.
0
f
;
return
tm
.
tm_gmtoff
/
60
;
#endif
#endif
}
}
...
...
include/c11log/details/pattern_formatter.h
View file @
e148b939
...
@@ -9,7 +9,6 @@
...
@@ -9,7 +9,6 @@
#include "./log_msg.h"
#include "./log_msg.h"
#include "./fast_oss.h"
#include "./fast_oss.h"
#include "./os.h"
#include "./os.h"
#include "Windows.h"
namespace
c11log
namespace
c11log
{
{
...
@@ -58,7 +57,7 @@ class a_formatter :public flag_formatter
...
@@ -58,7 +57,7 @@ class a_formatter :public flag_formatter
{
{
void
format
(
details
::
log_msg
&
msg
)
override
void
format
(
details
::
log_msg
&
msg
)
override
{
{
msg
.
formatted
.
write
_str
(
days
[
msg
.
tm_time
.
tm_wday
]);
msg
.
formatted
.
put
_str
(
days
[
msg
.
tm_time
.
tm_wday
]);
}
}
};
};
...
@@ -68,7 +67,7 @@ class A_formatter :public flag_formatter
...
@@ -68,7 +67,7 @@ class A_formatter :public flag_formatter
{
{
void
format
(
details
::
log_msg
&
msg
)
override
void
format
(
details
::
log_msg
&
msg
)
override
{
{
msg
.
formatted
.
write
_str
(
full_days
[
msg
.
tm_time
.
tm_wday
]);
msg
.
formatted
.
put
_str
(
full_days
[
msg
.
tm_time
.
tm_wday
]);
}
}
};
};
...
@@ -78,7 +77,7 @@ class b_formatter :public flag_formatter
...
@@ -78,7 +77,7 @@ class b_formatter :public flag_formatter
{
{
void
format
(
details
::
log_msg
&
msg
)
override
void
format
(
details
::
log_msg
&
msg
)
override
{
{
msg
.
formatted
.
write
_str
(
months
[
msg
.
tm_time
.
tm_mon
]);
msg
.
formatted
.
put
_str
(
months
[
msg
.
tm_time
.
tm_mon
]);
}
}
};
};
...
@@ -88,7 +87,7 @@ class B_formatter :public flag_formatter
...
@@ -88,7 +87,7 @@ class B_formatter :public flag_formatter
{
{
void
format
(
details
::
log_msg
&
msg
)
override
void
format
(
details
::
log_msg
&
msg
)
override
{
{
msg
.
formatted
.
write
_str
(
full_months
[
msg
.
tm_time
.
tm_mon
]);
msg
.
formatted
.
put
_str
(
full_months
[
msg
.
tm_time
.
tm_mon
]);
}
}
};
};
...
@@ -97,17 +96,17 @@ class c_formatter :public flag_formatter
...
@@ -97,17 +96,17 @@ class c_formatter :public flag_formatter
{
{
void
format
(
details
::
log_msg
&
msg
)
override
void
format
(
details
::
log_msg
&
msg
)
override
{
{
msg
.
formatted
.
write
_str
(
days
[
msg
.
tm_time
.
tm_wday
]);
msg
.
formatted
.
put
_str
(
days
[
msg
.
tm_time
.
tm_wday
]);
msg
.
formatted
.
putc
(
' '
);
msg
.
formatted
.
putc
(
' '
);
msg
.
formatted
.
write
_str
(
months
[
msg
.
tm_time
.
tm_mon
]);
msg
.
formatted
.
put
_str
(
months
[
msg
.
tm_time
.
tm_mon
]);
msg
.
formatted
.
putc
(
' '
);
msg
.
formatted
.
putc
(
' '
);
msg
.
formatted
.
write
_int
(
msg
.
tm_time
.
tm_mday
,
2
);
msg
.
formatted
.
put
_int
(
msg
.
tm_time
.
tm_mday
,
2
);
msg
.
formatted
.
putc
(
' '
);
msg
.
formatted
.
putc
(
' '
);
msg
.
formatted
.
write
_int
(
msg
.
tm_time
.
tm_hour
,
2
);
msg
.
formatted
.
put
_int
(
msg
.
tm_time
.
tm_hour
,
2
);
msg
.
formatted
.
putc
(
':'
);
msg
.
formatted
.
putc
(
':'
);
msg
.
formatted
.
write
_int
(
msg
.
tm_time
.
tm_min
,
2
);
msg
.
formatted
.
put
_int
(
msg
.
tm_time
.
tm_min
,
2
);
msg
.
formatted
.
putc
(
':'
);
msg
.
formatted
.
putc
(
':'
);
msg
.
formatted
.
write
_int
(
msg
.
tm_time
.
tm_sec
,
2
);
msg
.
formatted
.
put
_int
(
msg
.
tm_time
.
tm_sec
,
2
);
}
}
};
};
...
@@ -117,7 +116,7 @@ class C_formatter :public flag_formatter
...
@@ -117,7 +116,7 @@ class C_formatter :public flag_formatter
{
{
void
format
(
details
::
log_msg
&
msg
)
override
void
format
(
details
::
log_msg
&
msg
)
override
{
{
msg
.
formatted
.
write_int
(
msg
.
tm_time
.
tm_year
%
100
,
2
);
msg
.
formatted
.
put_int
(
msg
.
tm_time
.
tm_year
%
100
,
2
);
}
}
};
};
...
@@ -128,11 +127,11 @@ class D_formatter :public flag_formatter
...
@@ -128,11 +127,11 @@ class D_formatter :public flag_formatter
{
{
void
format
(
details
::
log_msg
&
msg
)
override
void
format
(
details
::
log_msg
&
msg
)
override
{
{
msg
.
formatted
.
write
_int
(
msg
.
tm_time
.
tm_mon
+
1
,
2
);
msg
.
formatted
.
put
_int
(
msg
.
tm_time
.
tm_mon
+
1
,
2
);
msg
.
formatted
.
putc
(
'/'
);
msg
.
formatted
.
putc
(
'/'
);
msg
.
formatted
.
write
_int
(
msg
.
tm_time
.
tm_mday
,
2
);
msg
.
formatted
.
put
_int
(
msg
.
tm_time
.
tm_mday
,
2
);
msg
.
formatted
.
putc
(
'/'
);
msg
.
formatted
.
putc
(
'/'
);
msg
.
formatted
.
write
_int
(
msg
.
tm_time
.
tm_year
%
100
,
2
);
msg
.
formatted
.
put
_int
(
msg
.
tm_time
.
tm_year
%
100
,
2
);
}
}
};
};
...
@@ -142,7 +141,7 @@ class Y_formatter :public flag_formatter
...
@@ -142,7 +141,7 @@ class Y_formatter :public flag_formatter
{
{
void
format
(
details
::
log_msg
&
msg
)
override
void
format
(
details
::
log_msg
&
msg
)
override
{
{
msg
.
formatted
.
write
_int
(
msg
.
tm_time
.
tm_year
+
1900
,
4
);
msg
.
formatted
.
put
_int
(
msg
.
tm_time
.
tm_year
+
1900
,
4
);
}
}
};
};
...
@@ -151,7 +150,7 @@ class m_formatter :public flag_formatter
...
@@ -151,7 +150,7 @@ class m_formatter :public flag_formatter
{
{
void
format
(
details
::
log_msg
&
msg
)
override
void
format
(
details
::
log_msg
&
msg
)
override
{
{
msg
.
formatted
.
write
_int
(
msg
.
tm_time
.
tm_mon
+
1
,
2
);
msg
.
formatted
.
put
_int
(
msg
.
tm_time
.
tm_mon
+
1
,
2
);
}
}
};
};
...
@@ -160,7 +159,7 @@ class d_formatter :public flag_formatter
...
@@ -160,7 +159,7 @@ class d_formatter :public flag_formatter
{
{
void
format
(
details
::
log_msg
&
msg
)
override
void
format
(
details
::
log_msg
&
msg
)
override
{
{
msg
.
formatted
.
write
_int
(
msg
.
tm_time
.
tm_mday
,
2
);
msg
.
formatted
.
put
_int
(
msg
.
tm_time
.
tm_mday
,
2
);
}
}
};
};
...
@@ -169,7 +168,7 @@ class H_formatter :public flag_formatter
...
@@ -169,7 +168,7 @@ class H_formatter :public flag_formatter
{
{
void
format
(
details
::
log_msg
&
msg
)
override
void
format
(
details
::
log_msg
&
msg
)
override
{
{
msg
.
formatted
.
write
_int
(
msg
.
tm_time
.
tm_hour
,
2
);
msg
.
formatted
.
put
_int
(
msg
.
tm_time
.
tm_hour
,
2
);
}
}
};
};
...
@@ -178,7 +177,7 @@ class I_formatter :public flag_formatter
...
@@ -178,7 +177,7 @@ class I_formatter :public flag_formatter
{
{
void
format
(
details
::
log_msg
&
msg
)
override
void
format
(
details
::
log_msg
&
msg
)
override
{
{
msg
.
formatted
.
write
_int
(
to12h
(
msg
.
tm_time
),
2
);
msg
.
formatted
.
put
_int
(
to12h
(
msg
.
tm_time
),
2
);
}
}
};
};
...
@@ -187,7 +186,7 @@ class M_formatter :public flag_formatter
...
@@ -187,7 +186,7 @@ class M_formatter :public flag_formatter
{
{
void
format
(
details
::
log_msg
&
msg
)
override
void
format
(
details
::
log_msg
&
msg
)
override
{
{
msg
.
formatted
.
write
_int
(
msg
.
tm_time
.
tm_min
,
2
);
msg
.
formatted
.
put
_int
(
msg
.
tm_time
.
tm_min
,
2
);
}
}
};
};
...
@@ -196,7 +195,7 @@ class S_formatter :public flag_formatter
...
@@ -196,7 +195,7 @@ class S_formatter :public flag_formatter
{
{
void
format
(
details
::
log_msg
&
msg
)
override
void
format
(
details
::
log_msg
&
msg
)
override
{
{
msg
.
formatted
.
write
_int
(
msg
.
tm_time
.
tm_sec
,
2
);
msg
.
formatted
.
put
_int
(
msg
.
tm_time
.
tm_sec
,
2
);
}
}
};
};
...
@@ -207,7 +206,7 @@ class e_formatter :public flag_formatter
...
@@ -207,7 +206,7 @@ class e_formatter :public flag_formatter
{
{
auto
duration
=
msg
.
time
.
time_since_epoch
();
auto
duration
=
msg
.
time
.
time_since_epoch
();
auto
millis
=
std
::
chrono
::
duration_cast
<
std
::
chrono
::
milliseconds
>
(
duration
).
count
()
%
1000
;
auto
millis
=
std
::
chrono
::
duration_cast
<
std
::
chrono
::
milliseconds
>
(
duration
).
count
()
%
1000
;
msg
.
formatted
.
write
_int
(
static_cast
<
int
>
(
millis
),
3
);
msg
.
formatted
.
put
_int
(
static_cast
<
int
>
(
millis
),
3
);
}
}
};
};
...
@@ -216,7 +215,7 @@ class p_formatter :public flag_formatter
...
@@ -216,7 +215,7 @@ class p_formatter :public flag_formatter
{
{
void
format
(
details
::
log_msg
&
msg
)
override
void
format
(
details
::
log_msg
&
msg
)
override
{
{
msg
.
formatted
.
write
_data
(
ampm
(
msg
.
tm_time
),
2
);
msg
.
formatted
.
put
_data
(
ampm
(
msg
.
tm_time
),
2
);
}
}
};
};
...
@@ -227,13 +226,13 @@ class r_formatter :public flag_formatter
...
@@ -227,13 +226,13 @@ class r_formatter :public flag_formatter
void
format
(
details
::
log_msg
&
msg
)
override
void
format
(
details
::
log_msg
&
msg
)
override
{
{
int
hours
=
to12h
(
msg
.
tm_time
);
int
hours
=
to12h
(
msg
.
tm_time
);
msg
.
formatted
.
write
_int
(
to12h
(
msg
.
tm_time
),
2
);
msg
.
formatted
.
put
_int
(
to12h
(
msg
.
tm_time
),
2
);
msg
.
formatted
.
putc
(
':'
);
msg
.
formatted
.
putc
(
':'
);
msg
.
formatted
.
write
_int
(
msg
.
tm_time
.
tm_min
,
2
);
msg
.
formatted
.
put
_int
(
msg
.
tm_time
.
tm_min
,
2
);
msg
.
formatted
.
putc
(
':'
);
msg
.
formatted
.
putc
(
':'
);
msg
.
formatted
.
write
_int
(
msg
.
tm_time
.
tm_sec
,
2
);
msg
.
formatted
.
put
_int
(
msg
.
tm_time
.
tm_sec
,
2
);
msg
.
formatted
.
putc
(
' '
);
msg
.
formatted
.
putc
(
' '
);
msg
.
formatted
.
write
_data
(
ampm
(
msg
.
tm_time
),
2
);
msg
.
formatted
.
put
_data
(
ampm
(
msg
.
tm_time
),
2
);
}
}
};
};
...
@@ -242,9 +241,9 @@ class R_formatter :public flag_formatter
...
@@ -242,9 +241,9 @@ class R_formatter :public flag_formatter
{
{
void
format
(
details
::
log_msg
&
msg
)
override
void
format
(
details
::
log_msg
&
msg
)
override
{
{
msg
.
formatted
.
write
_int
(
msg
.
tm_time
.
tm_hour
,
2
);
msg
.
formatted
.
put
_int
(
msg
.
tm_time
.
tm_hour
,
2
);
msg
.
formatted
.
putc
(
':'
);
msg
.
formatted
.
putc
(
':'
);
msg
.
formatted
.
write
_int
(
msg
.
tm_time
.
tm_min
,
2
);
msg
.
formatted
.
put
_int
(
msg
.
tm_time
.
tm_min
,
2
);
}
}
};
};
...
@@ -254,13 +253,48 @@ class T_formatter :public flag_formatter
...
@@ -254,13 +253,48 @@ class T_formatter :public flag_formatter
{
{
void
format
(
details
::
log_msg
&
msg
)
override
void
format
(
details
::
log_msg
&
msg
)
override
{
{
msg
.
formatted
.
write
_int
(
msg
.
tm_time
.
tm_hour
,
2
);
msg
.
formatted
.
put
_int
(
msg
.
tm_time
.
tm_hour
,
2
);
msg
.
formatted
.
putc
(
':'
);
msg
.
formatted
.
putc
(
':'
);
msg
.
formatted
.
write
_int
(
msg
.
tm_time
.
tm_min
,
2
);
msg
.
formatted
.
put
_int
(
msg
.
tm_time
.
tm_min
,
2
);
msg
.
formatted
.
putc
(
':'
);
msg
.
formatted
.
putc
(
':'
);
msg
.
formatted
.
write_int
(
msg
.
tm_time
.
tm_sec
,
2
);
msg
.
formatted
.
put_int
(
msg
.
tm_time
.
tm_sec
,
2
);
}
};
// ISO 8601 offset from UTC in timezone (HH:MM)
class
z_formatter
:
public
flag_formatter
{
public
:
void
format
(
log_msg
&
msg
)
override
{
std
::
lock_guard
<
std
::
mutex
>
l
(
_mutex
);
using
namespace
std
::
chrono
;
auto
diff
=
msg
.
time
-
_last_update
;
auto
secs_diff
=
abs
((
duration_cast
<
seconds
>
(
diff
)).
count
());
if
(
secs_diff
>=
2
)
{
_value
=
get_value
(
msg
);
_last_update
=
msg
.
time
;
}
}
msg
.
formatted
.
put_str
(
_value
);
}
private
:
log_clock
::
time_point
_last_update
;
std
::
string
_value
;
std
::
string
get_value
(
const
log_msg
&
msg
)
{
int
total_minutes
=
os
::
utc_minutes_offset
(
msg
.
tm_time
);
int
h
=
total_minutes
/
60
;
int
m
=
total_minutes
%
60
;
fast_oss
oss
;
oss
.
putc
(
h
<
0
?
'-'
:
'+'
);
oss
.
put_int
(
h
,
2
);
oss
.
putc
(
':'
);
oss
.
put_int
(
m
,
2
);
return
oss
.
str
();
}
std
::
mutex
_mutex
;
};
};
...
@@ -271,7 +305,7 @@ class t_formatter :public flag_formatter
...
@@ -271,7 +305,7 @@ class t_formatter :public flag_formatter
{
{
void
format
(
details
::
log_msg
&
msg
)
override
void
format
(
details
::
log_msg
&
msg
)
override
{
{
msg
.
formatted
.
write
_fast_oss
(
msg
.
raw
);
msg
.
formatted
.
put
_fast_oss
(
msg
.
raw
);
}
}
};
};
...
@@ -301,7 +335,7 @@ public:
...
@@ -301,7 +335,7 @@ public:
}
}
void
format
(
details
::
log_msg
&
msg
)
override
void
format
(
details
::
log_msg
&
msg
)
override
{
{
msg
.
formatted
.
write
_str
(
_str
);
msg
.
formatted
.
put
_str
(
_str
);
}
}
private
:
private
:
std
::
string
_str
;
std
::
string
_str
;
...
@@ -386,7 +420,7 @@ inline void c11log::details::pattern_formatter::handle_flag(char flag)
...
@@ -386,7 +420,7 @@ inline void c11log::details::pattern_formatter::handle_flag(char flag)
_formatters
.
push_back
(
std
::
unique_ptr
<
details
::
flag_formatter
>
(
new
details
::
A_formatter
()));
_formatters
.
push_back
(
std
::
unique_ptr
<
details
::
flag_formatter
>
(
new
details
::
A_formatter
()));
break
;
break
;
case
(
'b'
):
case
(
'b'
)
:
case
(
'h'
)
:
case
(
'h'
)
:
_formatters
.
push_back
(
std
::
unique_ptr
<
details
::
flag_formatter
>
(
new
details
::
b_formatter
()));
_formatters
.
push_back
(
std
::
unique_ptr
<
details
::
flag_formatter
>
(
new
details
::
b_formatter
()));
break
;
break
;
...
@@ -436,11 +470,11 @@ inline void c11log::details::pattern_formatter::handle_flag(char flag)
...
@@ -436,11 +470,11 @@ inline void c11log::details::pattern_formatter::handle_flag(char flag)
_formatters
.
push_back
(
std
::
unique_ptr
<
details
::
flag_formatter
>
(
new
details
::
S_formatter
()));
_formatters
.
push_back
(
std
::
unique_ptr
<
details
::
flag_formatter
>
(
new
details
::
S_formatter
()));
break
;
break
;
case
(
'e'
):
case
(
'e'
)
:
_formatters
.
push_back
(
std
::
unique_ptr
<
details
::
flag_formatter
>
(
new
details
::
e_formatter
()));
_formatters
.
push_back
(
std
::
unique_ptr
<
details
::
flag_formatter
>
(
new
details
::
e_formatter
()));
break
;
break
;
case
(
'p'
):
case
(
'p'
)
:
_formatters
.
push_back
(
std
::
unique_ptr
<
details
::
flag_formatter
>
(
new
details
::
p_formatter
()));
_formatters
.
push_back
(
std
::
unique_ptr
<
details
::
flag_formatter
>
(
new
details
::
p_formatter
()));
break
;
break
;
...
@@ -453,10 +487,14 @@ inline void c11log::details::pattern_formatter::handle_flag(char flag)
...
@@ -453,10 +487,14 @@ inline void c11log::details::pattern_formatter::handle_flag(char flag)
break
;
break
;
case
(
'T'
)
:
case
(
'T'
)
:
case
(
'X'
):
case
(
'X'
)
:
_formatters
.
push_back
(
std
::
unique_ptr
<
details
::
flag_formatter
>
(
new
details
::
T_formatter
()));
_formatters
.
push_back
(
std
::
unique_ptr
<
details
::
flag_formatter
>
(
new
details
::
T_formatter
()));
break
;
break
;
case
(
'z'
)
:
_formatters
.
push_back
(
std
::
unique_ptr
<
details
::
flag_formatter
>
(
new
details
::
z_formatter
()));
break
;
default
:
//Unkown flag appears as is
default
:
//Unkown flag appears as is
_formatters
.
push_back
(
std
::
unique_ptr
<
details
::
flag_formatter
>
(
new
details
::
ch_formatter
(
'%'
)));
_formatters
.
push_back
(
std
::
unique_ptr
<
details
::
flag_formatter
>
(
new
details
::
ch_formatter
(
'%'
)));
_formatters
.
push_back
(
std
::
unique_ptr
<
details
::
flag_formatter
>
(
new
details
::
ch_formatter
(
flag
)));
_formatters
.
push_back
(
std
::
unique_ptr
<
details
::
flag_formatter
>
(
new
details
::
ch_formatter
(
flag
)));
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment