Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
二次元的罪恶王冠
beego
提交
94ad13c8
B
beego
项目概览
二次元的罪恶王冠
/
beego
与 Fork 源项目一致
从无法访问的项目Fork
通知
1
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
B
beego
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
94ad13c8
编写于
12月 30, 2013
作者:
F
FuXiaoHei
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
add comments in logs package
上级
3118c6c2
变更
5
显示空白变更内容
内联
并排
Showing
5 changed file
with
73 addition
and
6 deletion
+73
-6
logs/conn.go
logs/conn.go
+9
-0
logs/console.go
logs/console.go
+7
-0
logs/file.go
logs/file.go
+18
-3
logs/log.go
logs/log.go
+22
-2
logs/smtp.go
logs/smtp.go
+17
-1
未找到文件。
logs/conn.go
浏览文件 @
94ad13c8
...
@@ -7,6 +7,8 @@ import (
...
@@ -7,6 +7,8 @@ import (
"net"
"net"
)
)
// ConnWriter implements LoggerInterface.
// it writes messages in keep-live tcp connection.
type
ConnWriter
struct
{
type
ConnWriter
struct
{
lg
*
log
.
Logger
lg
*
log
.
Logger
innerWriter
io
.
WriteCloser
innerWriter
io
.
WriteCloser
...
@@ -17,12 +19,15 @@ type ConnWriter struct {
...
@@ -17,12 +19,15 @@ type ConnWriter struct {
Level
int
`json:"level"`
Level
int
`json:"level"`
}
}
// create new ConnWrite returning as LoggerInterface.
func
NewConn
()
LoggerInterface
{
func
NewConn
()
LoggerInterface
{
conn
:=
new
(
ConnWriter
)
conn
:=
new
(
ConnWriter
)
conn
.
Level
=
LevelTrace
conn
.
Level
=
LevelTrace
return
conn
return
conn
}
}
// init connection writer with json config.
// json config only need key "level".
func
(
c
*
ConnWriter
)
Init
(
jsonconfig
string
)
error
{
func
(
c
*
ConnWriter
)
Init
(
jsonconfig
string
)
error
{
err
:=
json
.
Unmarshal
([]
byte
(
jsonconfig
),
c
)
err
:=
json
.
Unmarshal
([]
byte
(
jsonconfig
),
c
)
if
err
!=
nil
{
if
err
!=
nil
{
...
@@ -31,6 +36,8 @@ func (c *ConnWriter) Init(jsonconfig string) error {
...
@@ -31,6 +36,8 @@ func (c *ConnWriter) Init(jsonconfig string) error {
return
nil
return
nil
}
}
// write message in connection.
// if connection is down, try to re-connect.
func
(
c
*
ConnWriter
)
WriteMsg
(
msg
string
,
level
int
)
error
{
func
(
c
*
ConnWriter
)
WriteMsg
(
msg
string
,
level
int
)
error
{
if
level
<
c
.
Level
{
if
level
<
c
.
Level
{
return
nil
return
nil
...
@@ -49,10 +56,12 @@ func (c *ConnWriter) WriteMsg(msg string, level int) error {
...
@@ -49,10 +56,12 @@ func (c *ConnWriter) WriteMsg(msg string, level int) error {
return
nil
return
nil
}
}
// implementing method. empty.
func
(
c
*
ConnWriter
)
Flush
()
{
func
(
c
*
ConnWriter
)
Flush
()
{
}
}
// destroy connection writer and close tcp listener.
func
(
c
*
ConnWriter
)
Destroy
()
{
func
(
c
*
ConnWriter
)
Destroy
()
{
if
c
.
innerWriter
==
nil
{
if
c
.
innerWriter
==
nil
{
return
return
...
...
logs/console.go
浏览文件 @
94ad13c8
...
@@ -6,11 +6,13 @@ import (
...
@@ -6,11 +6,13 @@ import (
"os"
"os"
)
)
// ConsoleWriter implements LoggerInterface and writes messages to terminal.
type
ConsoleWriter
struct
{
type
ConsoleWriter
struct
{
lg
*
log
.
Logger
lg
*
log
.
Logger
Level
int
`json:"level"`
Level
int
`json:"level"`
}
}
// create ConsoleWriter returning as LoggerInterface.
func
NewConsole
()
LoggerInterface
{
func
NewConsole
()
LoggerInterface
{
cw
:=
new
(
ConsoleWriter
)
cw
:=
new
(
ConsoleWriter
)
cw
.
lg
=
log
.
New
(
os
.
Stdout
,
""
,
log
.
Ldate
|
log
.
Ltime
)
cw
.
lg
=
log
.
New
(
os
.
Stdout
,
""
,
log
.
Ldate
|
log
.
Ltime
)
...
@@ -18,6 +20,8 @@ func NewConsole() LoggerInterface {
...
@@ -18,6 +20,8 @@ func NewConsole() LoggerInterface {
return
cw
return
cw
}
}
// init console logger.
// jsonconfig like '{"level":LevelTrace}'.
func
(
c
*
ConsoleWriter
)
Init
(
jsonconfig
string
)
error
{
func
(
c
*
ConsoleWriter
)
Init
(
jsonconfig
string
)
error
{
err
:=
json
.
Unmarshal
([]
byte
(
jsonconfig
),
c
)
err
:=
json
.
Unmarshal
([]
byte
(
jsonconfig
),
c
)
if
err
!=
nil
{
if
err
!=
nil
{
...
@@ -26,6 +30,7 @@ func (c *ConsoleWriter) Init(jsonconfig string) error {
...
@@ -26,6 +30,7 @@ func (c *ConsoleWriter) Init(jsonconfig string) error {
return
nil
return
nil
}
}
// write message in console.
func
(
c
*
ConsoleWriter
)
WriteMsg
(
msg
string
,
level
int
)
error
{
func
(
c
*
ConsoleWriter
)
WriteMsg
(
msg
string
,
level
int
)
error
{
if
level
<
c
.
Level
{
if
level
<
c
.
Level
{
return
nil
return
nil
...
@@ -34,10 +39,12 @@ func (c *ConsoleWriter) WriteMsg(msg string, level int) error {
...
@@ -34,10 +39,12 @@ func (c *ConsoleWriter) WriteMsg(msg string, level int) error {
return
nil
return
nil
}
}
// implementing method. empty.
func
(
c
*
ConsoleWriter
)
Destroy
()
{
func
(
c
*
ConsoleWriter
)
Destroy
()
{
}
}
// implementing method. empty.
func
(
c
*
ConsoleWriter
)
Flush
()
{
func
(
c
*
ConsoleWriter
)
Flush
()
{
}
}
...
...
logs/file.go
浏览文件 @
94ad13c8
...
@@ -13,6 +13,8 @@ import (
...
@@ -13,6 +13,8 @@ import (
"time"
"time"
)
)
// FileLogWriter implements LoggerInterface.
// It writes messages by lines limit, file size limit, or time frequency.
type
FileLogWriter
struct
{
type
FileLogWriter
struct
{
*
log
.
Logger
*
log
.
Logger
mw
*
MuxWriter
mw
*
MuxWriter
...
@@ -38,17 +40,20 @@ type FileLogWriter struct {
...
@@ -38,17 +40,20 @@ type FileLogWriter struct {
Level
int
`json:"level"`
Level
int
`json:"level"`
}
}
// an *os.File writer with locker.
type
MuxWriter
struct
{
type
MuxWriter
struct
{
sync
.
Mutex
sync
.
Mutex
fd
*
os
.
File
fd
*
os
.
File
}
}
// write to os.File.
func
(
l
*
MuxWriter
)
Write
(
b
[]
byte
)
(
int
,
error
)
{
func
(
l
*
MuxWriter
)
Write
(
b
[]
byte
)
(
int
,
error
)
{
l
.
Lock
()
l
.
Lock
()
defer
l
.
Unlock
()
defer
l
.
Unlock
()
return
l
.
fd
.
Write
(
b
)
return
l
.
fd
.
Write
(
b
)
}
}
// set os.File in writer.
func
(
l
*
MuxWriter
)
SetFd
(
fd
*
os
.
File
)
{
func
(
l
*
MuxWriter
)
SetFd
(
fd
*
os
.
File
)
{
if
l
.
fd
!=
nil
{
if
l
.
fd
!=
nil
{
l
.
fd
.
Close
()
l
.
fd
.
Close
()
...
@@ -56,6 +61,7 @@ func (l *MuxWriter) SetFd(fd *os.File) {
...
@@ -56,6 +61,7 @@ func (l *MuxWriter) SetFd(fd *os.File) {
l
.
fd
=
fd
l
.
fd
=
fd
}
}
// create a FileLogWriter returning as LoggerInterface.
func
NewFileWriter
()
LoggerInterface
{
func
NewFileWriter
()
LoggerInterface
{
w
:=
&
FileLogWriter
{
w
:=
&
FileLogWriter
{
Filename
:
""
,
Filename
:
""
,
...
@@ -73,15 +79,16 @@ func NewFileWriter() LoggerInterface {
...
@@ -73,15 +79,16 @@ func NewFileWriter() LoggerInterface {
return
w
return
w
}
}
// jsonconfig like this
// Init file logger with json config.
//{
// jsonconfig like:
// {
// "filename":"logs/beego.log",
// "filename":"logs/beego.log",
// "maxlines":10000,
// "maxlines":10000,
// "maxsize":1<<30,
// "maxsize":1<<30,
// "daily":true,
// "daily":true,
// "maxdays":15,
// "maxdays":15,
// "rotate":true
// "rotate":true
//}
//
}
func
(
w
*
FileLogWriter
)
Init
(
jsonconfig
string
)
error
{
func
(
w
*
FileLogWriter
)
Init
(
jsonconfig
string
)
error
{
err
:=
json
.
Unmarshal
([]
byte
(
jsonconfig
),
w
)
err
:=
json
.
Unmarshal
([]
byte
(
jsonconfig
),
w
)
if
err
!=
nil
{
if
err
!=
nil
{
...
@@ -94,6 +101,7 @@ func (w *FileLogWriter) Init(jsonconfig string) error {
...
@@ -94,6 +101,7 @@ func (w *FileLogWriter) Init(jsonconfig string) error {
return
err
return
err
}
}
// start file logger. create log file and set to locker-inside file writer.
func
(
w
*
FileLogWriter
)
StartLogger
()
error
{
func
(
w
*
FileLogWriter
)
StartLogger
()
error
{
fd
,
err
:=
w
.
createLogFile
()
fd
,
err
:=
w
.
createLogFile
()
if
err
!=
nil
{
if
err
!=
nil
{
...
@@ -122,6 +130,7 @@ func (w *FileLogWriter) docheck(size int) {
...
@@ -122,6 +130,7 @@ func (w *FileLogWriter) docheck(size int) {
w
.
maxsize_cursize
+=
size
w
.
maxsize_cursize
+=
size
}
}
// write logger message into file.
func
(
w
*
FileLogWriter
)
WriteMsg
(
msg
string
,
level
int
)
error
{
func
(
w
*
FileLogWriter
)
WriteMsg
(
msg
string
,
level
int
)
error
{
if
level
<
w
.
Level
{
if
level
<
w
.
Level
{
return
nil
return
nil
...
@@ -158,6 +167,8 @@ func (w *FileLogWriter) initFd() error {
...
@@ -158,6 +167,8 @@ func (w *FileLogWriter) initFd() error {
return
nil
return
nil
}
}
// DoRotate means it need to write file in new file.
// new file name like xx.log.2013-01-01.2
func
(
w
*
FileLogWriter
)
DoRotate
()
error
{
func
(
w
*
FileLogWriter
)
DoRotate
()
error
{
_
,
err
:=
os
.
Lstat
(
w
.
Filename
)
_
,
err
:=
os
.
Lstat
(
w
.
Filename
)
if
err
==
nil
{
// file exists
if
err
==
nil
{
// file exists
...
@@ -211,10 +222,14 @@ func (w *FileLogWriter) deleteOldLog() {
...
@@ -211,10 +222,14 @@ func (w *FileLogWriter) deleteOldLog() {
})
})
}
}
// destroy file logger, close file writer.
func
(
w
*
FileLogWriter
)
Destroy
()
{
func
(
w
*
FileLogWriter
)
Destroy
()
{
w
.
mw
.
fd
.
Close
()
w
.
mw
.
fd
.
Close
()
}
}
// flush file logger.
// there are no buffering messages in file logger in memory.
// flush file means sync file from disk.
func
(
w
*
FileLogWriter
)
Flush
()
{
func
(
w
*
FileLogWriter
)
Flush
()
{
w
.
mw
.
fd
.
Sync
()
w
.
mw
.
fd
.
Sync
()
}
}
...
...
logs/log.go
浏览文件 @
94ad13c8
...
@@ -6,6 +6,7 @@ import (
...
@@ -6,6 +6,7 @@ import (
)
)
const
(
const
(
// log message levels
LevelTrace
=
iota
LevelTrace
=
iota
LevelDebug
LevelDebug
LevelInfo
LevelInfo
...
@@ -16,6 +17,7 @@ const (
...
@@ -16,6 +17,7 @@ const (
type
loggerType
func
()
LoggerInterface
type
loggerType
func
()
LoggerInterface
// LoggerInterface defines the behavior of a log provider.
type
LoggerInterface
interface
{
type
LoggerInterface
interface
{
Init
(
config
string
)
error
Init
(
config
string
)
error
WriteMsg
(
msg
string
,
level
int
)
error
WriteMsg
(
msg
string
,
level
int
)
error
...
@@ -38,6 +40,8 @@ func Register(name string, log loggerType) {
...
@@ -38,6 +40,8 @@ func Register(name string, log loggerType) {
adapters
[
name
]
=
log
adapters
[
name
]
=
log
}
}
// BeeLogger is default logger in beego application.
// it can contain several providers and log message into all providers.
type
BeeLogger
struct
{
type
BeeLogger
struct
{
lock
sync
.
Mutex
lock
sync
.
Mutex
level
int
level
int
...
@@ -50,7 +54,9 @@ type logMsg struct {
...
@@ -50,7 +54,9 @@ type logMsg struct {
msg
string
msg
string
}
}
// config need to be correct JSON as string: {"interval":360}
// NewLogger returns a new BeeLogger.
// channellen means the number of messages in chan.
// if the buffering chan is full, logger adapters write to file or other way.
func
NewLogger
(
channellen
int64
)
*
BeeLogger
{
func
NewLogger
(
channellen
int64
)
*
BeeLogger
{
bl
:=
new
(
BeeLogger
)
bl
:=
new
(
BeeLogger
)
bl
.
msg
=
make
(
chan
*
logMsg
,
channellen
)
bl
.
msg
=
make
(
chan
*
logMsg
,
channellen
)
...
@@ -60,6 +66,8 @@ func NewLogger(channellen int64) *BeeLogger {
...
@@ -60,6 +66,8 @@ func NewLogger(channellen int64) *BeeLogger {
return
bl
return
bl
}
}
// SetLogger provides a given logger adapter into BeeLogger with config string.
// config need to be correct JSON as string: {"interval":360}.
func
(
bl
*
BeeLogger
)
SetLogger
(
adaptername
string
,
config
string
)
error
{
func
(
bl
*
BeeLogger
)
SetLogger
(
adaptername
string
,
config
string
)
error
{
bl
.
lock
.
Lock
()
bl
.
lock
.
Lock
()
defer
bl
.
lock
.
Unlock
()
defer
bl
.
lock
.
Unlock
()
...
@@ -73,6 +81,7 @@ func (bl *BeeLogger) SetLogger(adaptername string, config string) error {
...
@@ -73,6 +81,7 @@ func (bl *BeeLogger) SetLogger(adaptername string, config string) error {
}
}
}
}
// remove a logger adapter in BeeLogger.
func
(
bl
*
BeeLogger
)
DelLogger
(
adaptername
string
)
error
{
func
(
bl
*
BeeLogger
)
DelLogger
(
adaptername
string
)
error
{
bl
.
lock
.
Lock
()
bl
.
lock
.
Lock
()
defer
bl
.
lock
.
Unlock
()
defer
bl
.
lock
.
Unlock
()
...
@@ -96,10 +105,14 @@ func (bl *BeeLogger) writerMsg(loglevel int, msg string) error {
...
@@ -96,10 +105,14 @@ func (bl *BeeLogger) writerMsg(loglevel int, msg string) error {
return
nil
return
nil
}
}
// set log message level.
// if message level (such as LevelTrace) is less than logger level (such as LevelWarn), ignore message.
func
(
bl
*
BeeLogger
)
SetLevel
(
l
int
)
{
func
(
bl
*
BeeLogger
)
SetLevel
(
l
int
)
{
bl
.
level
=
l
bl
.
level
=
l
}
}
// start logger chan reading.
// when chan is full, write logs.
func
(
bl
*
BeeLogger
)
StartLogger
()
{
func
(
bl
*
BeeLogger
)
StartLogger
()
{
for
{
for
{
select
{
select
{
...
@@ -111,43 +124,50 @@ func (bl *BeeLogger) StartLogger() {
...
@@ -111,43 +124,50 @@ func (bl *BeeLogger) StartLogger() {
}
}
}
}
// log trace level message.
func
(
bl
*
BeeLogger
)
Trace
(
format
string
,
v
...
interface
{})
{
func
(
bl
*
BeeLogger
)
Trace
(
format
string
,
v
...
interface
{})
{
msg
:=
fmt
.
Sprintf
(
"[T] "
+
format
,
v
...
)
msg
:=
fmt
.
Sprintf
(
"[T] "
+
format
,
v
...
)
bl
.
writerMsg
(
LevelTrace
,
msg
)
bl
.
writerMsg
(
LevelTrace
,
msg
)
}
}
// log debug level message.
func
(
bl
*
BeeLogger
)
Debug
(
format
string
,
v
...
interface
{})
{
func
(
bl
*
BeeLogger
)
Debug
(
format
string
,
v
...
interface
{})
{
msg
:=
fmt
.
Sprintf
(
"[D] "
+
format
,
v
...
)
msg
:=
fmt
.
Sprintf
(
"[D] "
+
format
,
v
...
)
bl
.
writerMsg
(
LevelDebug
,
msg
)
bl
.
writerMsg
(
LevelDebug
,
msg
)
}
}
// log info level message.
func
(
bl
*
BeeLogger
)
Info
(
format
string
,
v
...
interface
{})
{
func
(
bl
*
BeeLogger
)
Info
(
format
string
,
v
...
interface
{})
{
msg
:=
fmt
.
Sprintf
(
"[I] "
+
format
,
v
...
)
msg
:=
fmt
.
Sprintf
(
"[I] "
+
format
,
v
...
)
bl
.
writerMsg
(
LevelInfo
,
msg
)
bl
.
writerMsg
(
LevelInfo
,
msg
)
}
}
// log warn level message.
func
(
bl
*
BeeLogger
)
Warn
(
format
string
,
v
...
interface
{})
{
func
(
bl
*
BeeLogger
)
Warn
(
format
string
,
v
...
interface
{})
{
msg
:=
fmt
.
Sprintf
(
"[W] "
+
format
,
v
...
)
msg
:=
fmt
.
Sprintf
(
"[W] "
+
format
,
v
...
)
bl
.
writerMsg
(
LevelWarn
,
msg
)
bl
.
writerMsg
(
LevelWarn
,
msg
)
}
}
// log error level message.
func
(
bl
*
BeeLogger
)
Error
(
format
string
,
v
...
interface
{})
{
func
(
bl
*
BeeLogger
)
Error
(
format
string
,
v
...
interface
{})
{
msg
:=
fmt
.
Sprintf
(
"[E] "
+
format
,
v
...
)
msg
:=
fmt
.
Sprintf
(
"[E] "
+
format
,
v
...
)
bl
.
writerMsg
(
LevelError
,
msg
)
bl
.
writerMsg
(
LevelError
,
msg
)
}
}
// log critical level message.
func
(
bl
*
BeeLogger
)
Critical
(
format
string
,
v
...
interface
{})
{
func
(
bl
*
BeeLogger
)
Critical
(
format
string
,
v
...
interface
{})
{
msg
:=
fmt
.
Sprintf
(
"[C] "
+
format
,
v
...
)
msg
:=
fmt
.
Sprintf
(
"[C] "
+
format
,
v
...
)
bl
.
writerMsg
(
LevelCritical
,
msg
)
bl
.
writerMsg
(
LevelCritical
,
msg
)
}
}
//
flush all chan data
//
flush all chan data.
func
(
bl
*
BeeLogger
)
Flush
()
{
func
(
bl
*
BeeLogger
)
Flush
()
{
for
_
,
l
:=
range
bl
.
outputs
{
for
_
,
l
:=
range
bl
.
outputs
{
l
.
Flush
()
l
.
Flush
()
}
}
}
}
// close logger, flush all chan data and destroy all adapters in BeeLogger.
func
(
bl
*
BeeLogger
)
Close
()
{
func
(
bl
*
BeeLogger
)
Close
()
{
for
{
for
{
if
len
(
bl
.
msg
)
>
0
{
if
len
(
bl
.
msg
)
>
0
{
...
...
logs/smtp.go
浏览文件 @
94ad13c8
...
@@ -12,7 +12,7 @@ const (
...
@@ -12,7 +12,7 @@ const (
subjectPhrase
=
"Diagnostic message from server"
subjectPhrase
=
"Diagnostic message from server"
)
)
// smtpWriter is used to send emails via given SMTP-server.
// smtpWriter i
mplements LoggerInterface and i
s used to send emails via given SMTP-server.
type
SmtpWriter
struct
{
type
SmtpWriter
struct
{
Username
string
`json:"Username"`
Username
string
`json:"Username"`
Password
string
`json:"password"`
Password
string
`json:"password"`
...
@@ -22,10 +22,21 @@ type SmtpWriter struct {
...
@@ -22,10 +22,21 @@ type SmtpWriter struct {
Level
int
`json:"level"`
Level
int
`json:"level"`
}
}
// create smtp writer.
func
NewSmtpWriter
()
LoggerInterface
{
func
NewSmtpWriter
()
LoggerInterface
{
return
&
SmtpWriter
{
Level
:
LevelTrace
}
return
&
SmtpWriter
{
Level
:
LevelTrace
}
}
}
// init smtp writer with json config.
// config like:
// {
// "Username":"example@gmail.com",
// "password:"password",
// "host":"smtp.gmail.com:465",
// "subject":"email title",
// "sendTos":["email1","email2"],
// "level":LevelError
// }
func
(
s
*
SmtpWriter
)
Init
(
jsonconfig
string
)
error
{
func
(
s
*
SmtpWriter
)
Init
(
jsonconfig
string
)
error
{
err
:=
json
.
Unmarshal
([]
byte
(
jsonconfig
),
s
)
err
:=
json
.
Unmarshal
([]
byte
(
jsonconfig
),
s
)
if
err
!=
nil
{
if
err
!=
nil
{
...
@@ -34,6 +45,8 @@ func (s *SmtpWriter) Init(jsonconfig string) error {
...
@@ -34,6 +45,8 @@ func (s *SmtpWriter) Init(jsonconfig string) error {
return
nil
return
nil
}
}
// write message in smtp writer.
// it will send an email with subject and only this message.
func
(
s
*
SmtpWriter
)
WriteMsg
(
msg
string
,
level
int
)
error
{
func
(
s
*
SmtpWriter
)
WriteMsg
(
msg
string
,
level
int
)
error
{
if
level
<
s
.
Level
{
if
level
<
s
.
Level
{
return
nil
return
nil
...
@@ -65,9 +78,12 @@ func (s *SmtpWriter) WriteMsg(msg string, level int) error {
...
@@ -65,9 +78,12 @@ func (s *SmtpWriter) WriteMsg(msg string, level int) error {
return
err
return
err
}
}
// implementing method. empty.
func
(
s
*
SmtpWriter
)
Flush
()
{
func
(
s
*
SmtpWriter
)
Flush
()
{
return
return
}
}
// implementing method. empty.
func
(
s
*
SmtpWriter
)
Destroy
()
{
func
(
s
*
SmtpWriter
)
Destroy
()
{
return
return
}
}
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录