187 lines
5.1 KiB
Go
187 lines
5.1 KiB
Go
// Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
|
|
// isulad-tools is licensed under the Mulan PSL v1.
|
|
// You can use this software according to the terms and conditions of the Mulan PSL v1.
|
|
// You may obtain a copy of Mulan PSL v1 at:
|
|
// http://license.coscl.org.cn/MulanPSL
|
|
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
// PURPOSE.
|
|
// See the Mulan PSL v1 for more details.
|
|
// Description: common utils
|
|
// Author: zhangwei
|
|
// Create: 2018-01-18
|
|
|
|
package utils
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"log/syslog"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"syscall"
|
|
"time"
|
|
|
|
libctr_utils "github.com/opencontainers/runc/libcontainer/utils"
|
|
"github.com/sirupsen/logrus"
|
|
)
|
|
|
|
// SyslogHook to send logs via syslog.
|
|
type syslogHook struct {
|
|
logger *log.Logger
|
|
}
|
|
|
|
// Creates a hook to be added to an instance of logger. This is called with
|
|
// `hook, err := newSyslogHook("default", "udp", "localhost:514", syslog.LOG_DEBUG, "")`
|
|
// `if err == nil { log.Hooks.Add(hook) }`
|
|
func newSyslogHook(network, raddr string, priority syslog.Priority, tag string) (*syslogHook, error) {
|
|
var logger *log.Logger
|
|
var err error
|
|
|
|
if network == "default" {
|
|
logger, err = syslog.NewLogger(priority, log.Lshortfile)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
logger.SetPrefix(tag)
|
|
} else {
|
|
w, err := syslog.Dial(network, raddr, priority, "")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
logger = log.New(w, tag, log.Lshortfile)
|
|
}
|
|
|
|
return &syslogHook{logger}, err
|
|
}
|
|
|
|
func (hook *syslogHook) Fire(entry *logrus.Entry) error {
|
|
line, err := entry.String()
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "Unable to read entry, %v", err)
|
|
return err
|
|
}
|
|
if err := hook.logger.Output(8, line); err != nil {
|
|
logrus.Errorf("hook.logger.Output err: %s", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (hook *syslogHook) Levels() []logrus.Level {
|
|
return logrus.AllLevels
|
|
}
|
|
|
|
const (
|
|
syslogUDPPrefix = "udp://"
|
|
syslogTCPPrefix = "tcp://"
|
|
syslogUnixSock = "unix://"
|
|
syslogDefaultUDPService = "localhost:541"
|
|
syslogDefaultTCPService = "localhost:541"
|
|
)
|
|
|
|
// SyslogService is a structure which records the syslog service type and serivce address.
|
|
type SyslogService struct {
|
|
Type string
|
|
Addr string
|
|
}
|
|
|
|
// ParseSyslogService parses syslog service from input string
|
|
func ParseSyslogService(service string) (*SyslogService, error) {
|
|
var serviceType, serviceAddr string
|
|
|
|
if service == "" {
|
|
serviceType = "default"
|
|
serviceAddr = ""
|
|
} else if strings.HasPrefix(service, syslogUDPPrefix) {
|
|
serviceType = "udp"
|
|
serviceAddr := service[len(syslogUDPPrefix):]
|
|
if serviceAddr == "" {
|
|
serviceAddr = syslogDefaultUDPService
|
|
}
|
|
} else if strings.HasPrefix(service, syslogTCPPrefix) {
|
|
serviceType = "tcp"
|
|
serviceAddr = service[len(syslogTCPPrefix):]
|
|
if serviceAddr == "" {
|
|
serviceAddr = syslogDefaultTCPService
|
|
}
|
|
} else if strings.HasPrefix(service, syslogUnixSock) {
|
|
// syslog package will use empty string as network,
|
|
// and syslog will lookup the unix socket on host, we do not care.
|
|
serviceType = ""
|
|
serviceAddr = service[len(syslogUnixSock):]
|
|
} else {
|
|
return nil, fmt.Errorf("Unspported syslog network: %s", service)
|
|
}
|
|
|
|
serv := &SyslogService{
|
|
Type: serviceType,
|
|
Addr: serviceAddr,
|
|
}
|
|
return serv, nil
|
|
}
|
|
|
|
// HookSyslog will hook syslog service to logrus
|
|
// syslog supports 4 kinds of service:
|
|
// 1. default socket: ""
|
|
// 2. unix socket: "unix:///dev/log"
|
|
// 3. udp port: "udp://localhost:541"
|
|
// 4. tcp port: "tcp://localhost:541"
|
|
// by default, if we output to local syslog, use default will be fine.
|
|
// syslog Tag:
|
|
// syslog will use tag to separate the output stream.
|
|
func HookSyslog(service, tag string) error {
|
|
serv, err := ParseSyslogService(service)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
hook, err := newSyslogHook(serv.Type, serv.Addr, syslog.LOG_INFO|syslog.LOG_USER, tag)
|
|
if err != nil {
|
|
return fmt.Errorf("Unable to connect to syslog daemon")
|
|
}
|
|
logrus.AddHook(hook)
|
|
return nil
|
|
}
|
|
|
|
// NewPipe creates a pair of new socket pipe.
|
|
func NewPipe() (parent, child *os.File, err error) {
|
|
fds, err := syscall.Socketpair(syscall.AF_LOCAL, syscall.SOCK_STREAM|syscall.SOCK_CLOEXEC, 0)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
return os.NewFile(uintptr(fds[1]), "parent"), os.NewFile(uintptr(fds[0]), "child"), nil
|
|
}
|
|
|
|
// WriteJSON write json data to io stream
|
|
func WriteJSON(w io.Writer, v interface{}) error {
|
|
return libctr_utils.WriteJSON(w, v)
|
|
}
|
|
|
|
// RandomID returns a 8-bit ramdon string which read from rand.Reader first,
|
|
// and if failed, will use time stamp as random id
|
|
func RandomID() string {
|
|
id := make([]byte, 32)
|
|
if _, err := io.ReadFull(rand.Reader, id); err != nil {
|
|
cur := time.Now()
|
|
return fmt.Sprint(cur.UnixNano())
|
|
}
|
|
return hex.EncodeToString(id)[:8]
|
|
}
|
|
|
|
// RandomFile will find a non-existing file in given folder.
|
|
func RandomFile(folder string) string {
|
|
path := ""
|
|
for {
|
|
id := RandomID()
|
|
path = filepath.Join(folder, id)
|
|
if _, err := os.Stat(path); os.IsNotExist(err) {
|
|
break
|
|
}
|
|
}
|
|
return path
|
|
}
|