You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

763 lines
25 KiB

  1. // Copyright (c) 2016 Pani Networks
  2. // All Rights Reserved.
  3. //
  4. // Licensed under the Apache License, Version 2.0 (the "License"); you may
  5. // not use this file except in compliance with the License. You may obtain
  6. // a copy of the License at
  7. //
  8. // http://www.apache.org/licenses/LICENSE-2.0
  9. //
  10. // Unless required by applicable law or agreed to in writing, software
  11. // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. // License for the specific language governing permissions and limitations
  14. // under the License.
  15. package rlog
  16. import (
  17. "bufio"
  18. "bytes"
  19. "fmt"
  20. "io"
  21. "log"
  22. "os"
  23. "path"
  24. "path/filepath"
  25. "runtime"
  26. "strconv"
  27. "strings"
  28. "sync"
  29. "time"
  30. )
  31. // A few constants, which are used more like flags
  32. const (
  33. notATrace = -1
  34. noTraceOutput = -1
  35. )
  36. // The known log levels
  37. const (
  38. levelNone = iota
  39. levelCrit
  40. levelErr
  41. levelWarn
  42. levelInfo
  43. levelDebug
  44. levelTrace
  45. )
  46. // Translation map from level to string representation
  47. var levelStrings = map[int]string{
  48. levelTrace: "TRACE",
  49. levelDebug: "DEBUG",
  50. levelInfo: "INFO",
  51. levelWarn: "WARN",
  52. levelErr: "ERROR",
  53. levelCrit: "CRITICAL",
  54. levelNone: "NONE",
  55. }
  56. // Translation from level string to number.
  57. var levelNumbers = map[string]int{
  58. "TRACE": levelTrace,
  59. "DEBUG": levelDebug,
  60. "INFO": levelInfo,
  61. "WARN": levelWarn,
  62. "ERROR": levelErr,
  63. "CRITICAL": levelCrit,
  64. "NONE": levelNone,
  65. }
  66. // filterSpec holds a list of filters. These are applied to the 'caller'
  67. // information of a log message (calling module and file) to see if this
  68. // message should be logged. Different log or trace levels per file can
  69. // therefore be maintained. For log messages this is the log level, for trace
  70. // messages this is going to be the trace level.
  71. type filterSpec struct {
  72. filters []filter
  73. }
  74. // filter holds filename and level to match logs against log messages.
  75. type filter struct {
  76. Pattern string
  77. Level int
  78. }
  79. // rlogConfig captures the entire configuration of rlog, as supplied by a user
  80. // via environment variables and/or config files. This still requires checking
  81. // and translation into more easily used config items. All values therefore are
  82. // stored as simple strings here.
  83. type rlogConfig struct {
  84. logLevel string // What log level. String, since filters are allowed
  85. traceLevel string // What trace level. String, since filters are allowed
  86. logTimeFormat string // The time format spec for date/time stamps in output
  87. logFile string // Name of logfile
  88. confFile string // Name of config file
  89. logStream string // Name of logstream: stdout, stderr or NONE
  90. logNoTime string // Flag to determine if date/time is logged at all
  91. showCallerInfo string // Flag to determine if caller info is logged
  92. showGoroutineID string // Flag to determine if goroute ID shows in caller info
  93. confCheckInterv string // Interval in seconds for checking config file
  94. }
  95. // We keep a copy of what was supplied via environment variables, since we will
  96. // consult this every time we read from a config file. This allows us to
  97. // determine which values take precedence.
  98. var configFromEnvVars rlogConfig
  99. // The configuration items in rlogConfig are what is supplied by the user
  100. // (usually via environment variables). They are not the actual running
  101. // configuration. We interpret this, combine it with configuration from the
  102. // config file and produce pre-processed configuration values, which are stored
  103. // in those variables below.
  104. var (
  105. settingShowCallerInfo bool // whether we log caller info
  106. settingShowGoroutineID bool // whether we show goroutine ID in caller info
  107. settingDateTimeFormat string // flags for date/time output
  108. settingConfFile string // config file name
  109. // how often we check the conf file
  110. settingCheckInterval time.Duration = 15 * time.Second
  111. logWriterStream *log.Logger // the first writer to which output is sent
  112. logWriterFile *log.Logger // the second writer to which output is sent
  113. logFilterSpec *filterSpec // filters for log messages
  114. traceFilterSpec *filterSpec // filters for trace messages
  115. lastConfigFileCheck time.Time // when did we last check the config file
  116. currentLogFile *os.File // the logfile currently in use
  117. currentLogFileName string // name of current log file
  118. initMutex sync.RWMutex = sync.RWMutex{} // used to protect the init section
  119. )
  120. // fromString initializes filterSpec from string.
  121. //
  122. // Use the isTraceLevel flag to indicate whether the levels are numeric (for
  123. // trace messages) or are level strings (for log messages).
  124. //
  125. // Format "<filter>,<filter>,[<filter>]..."
  126. // filter:
  127. // <pattern=level> | <level>
  128. // pattern:
  129. // shell glob to match caller file name
  130. // level:
  131. // log or trace level of the logs to enable in matched files.
  132. //
  133. // Example:
  134. // - "RLOG_TRACE_LEVEL=3"
  135. // Just a global trace level of 3 for all files and modules.
  136. // - "RLOG_TRACE_LEVEL=client.go=1,ip*=5,3"
  137. // This enables trace level 1 in client.go, level 5 in all files whose
  138. // names start with 'ip', and level 3 for everyone else.
  139. // - "RLOG_LOG_LEVEL=DEBUG"
  140. // Global log level DEBUG for all files and modules.
  141. // - "RLOG_LOG_LEVEL=client.go=ERROR,INFO,ip*=WARN"
  142. // ERROR and higher for client.go, WARN or higher for all files whose
  143. // name starts with 'ip', INFO for everyone else.
  144. func (spec *filterSpec) fromString(s string, isTraceLevels bool, globalLevelDefault int) {
  145. var globalLevel int = globalLevelDefault
  146. var levelToken string
  147. var matchToken string
  148. fields := strings.Split(s, ",")
  149. for _, f := range fields {
  150. var filterLevel int
  151. var err error
  152. var ok bool
  153. // Tokens should contain two elements: The filename and the trace
  154. // level. If there is only one token then we have to assume that this
  155. // is the 'global' filter (without filename component).
  156. tokens := strings.Split(f, "=")
  157. if len(tokens) == 1 {
  158. // Global level. We'll store this one for the end, since it needs
  159. // to sit last in the list of filters (during evaluation in gets
  160. // checked last).
  161. matchToken = ""
  162. levelToken = tokens[0]
  163. } else if len(tokens) == 2 {
  164. matchToken = tokens[0]
  165. levelToken = tokens[1]
  166. } else {
  167. // Skip anything else that's malformed
  168. rlogIssue("Malformed log filter expression: '%s'", f)
  169. continue
  170. }
  171. if isTraceLevels {
  172. // The level token should contain a numeric value
  173. if filterLevel, err = strconv.Atoi(levelToken); err != nil {
  174. if levelToken != "" {
  175. rlogIssue("Trace level '%s' is not a number.", levelToken)
  176. }
  177. continue
  178. }
  179. } else {
  180. // The level token should contain the name of a log level
  181. levelToken = strings.ToUpper(levelToken)
  182. filterLevel, ok = levelNumbers[levelToken]
  183. if !ok || filterLevel == levelTrace {
  184. // User not allowed to set trace log levels, so if that or
  185. // not a known log level then this specification will be
  186. // ignored.
  187. if levelToken != "" {
  188. rlogIssue("Illegal log level '%s'.", levelToken)
  189. }
  190. continue
  191. }
  192. }
  193. if matchToken == "" {
  194. // Global level just remembered for now, not yet added
  195. globalLevel = filterLevel
  196. } else {
  197. spec.filters = append(spec.filters, filter{matchToken, filterLevel})
  198. }
  199. }
  200. // Now add the global level, so that later it will be evaluated last.
  201. // For trace levels we do something extra: There are possibly many trace
  202. // messages, but most often trace level debugging is fully disabled. We
  203. // want to optimize this. Therefore, a globalLevel of -1 (no trace levels)
  204. // isn't stored in the filter chain. If no other trace filters were defined
  205. // then this means the filter chain is empty, which can be tested very
  206. // efficiently in the top-level trace functions for an early exit.
  207. if !isTraceLevels || globalLevel != noTraceOutput {
  208. spec.filters = append(spec.filters, filter{"", globalLevel})
  209. }
  210. return
  211. }
  212. // matchfilters checks if given filename and trace level are accepted
  213. // by any of the filters
  214. func (spec *filterSpec) matchfilters(filename string, level int) bool {
  215. // If there are no filters then we don't match anything.
  216. if len(spec.filters) == 0 {
  217. return false
  218. }
  219. // If at least one filter matches.
  220. for _, filter := range spec.filters {
  221. if matched, loggit := filter.match(filename, level); matched {
  222. return loggit
  223. }
  224. }
  225. return false
  226. }
  227. // match checks if given filename and level are matched by
  228. // this filter. Returns two bools: One to indicate whether a filename match was
  229. // made, and the second to indicate whether the message should be logged
  230. // (matched the level).
  231. func (f filter) match(filename string, level int) (bool, bool) {
  232. var match bool
  233. if f.Pattern != "" {
  234. match, _ = filepath.Match(f.Pattern, filepath.Base(filename))
  235. } else {
  236. match = true
  237. }
  238. if match {
  239. return true, level <= f.Level
  240. }
  241. return false, false
  242. }
  243. // updateIfNeeded returns a new value for an existing config item. The priority
  244. // flag indicates whether the new value should always override the old value.
  245. // Otherwise, the new value will not be used in case the old value is already
  246. // set.
  247. func updateIfNeeded(oldVal string, newVal string, priority bool) string {
  248. if priority || oldVal == "" {
  249. return newVal
  250. }
  251. return oldVal
  252. }
  253. // updateConfigFromFile reads a configuration from the specified config file.
  254. // It merges the supplied config with the new values.
  255. func updateConfigFromFile(config *rlogConfig) {
  256. lastConfigFileCheck = time.Now()
  257. settingConfFile = config.confFile
  258. // If no config file was specified we will default to a known location.
  259. if settingConfFile == "" {
  260. execName := filepath.Base(os.Args[0])
  261. settingConfFile = fmt.Sprintf("/etc/rlog/%s.conf", execName)
  262. }
  263. // Scan over the config file, line by line
  264. file, err := os.Open(settingConfFile)
  265. if err != nil {
  266. // Any error while attempting to open the logfile ignored. In many
  267. // cases there won't even be a config file, so we should not produce
  268. // any noise.
  269. return
  270. }
  271. defer file.Close()
  272. scanner := bufio.NewScanner(file)
  273. i := 0
  274. for scanner.Scan() {
  275. i++
  276. line := strings.TrimSpace(scanner.Text())
  277. if line == "" || line[0] == '#' {
  278. continue
  279. }
  280. tokens := strings.SplitN(line, "=", 2)
  281. if len(tokens) == 0 {
  282. continue
  283. }
  284. if len(tokens) != 2 {
  285. rlogIssue("Malformed line in config file %s:%d. Ignored.",
  286. settingConfFile, i)
  287. continue
  288. }
  289. name := strings.TrimSpace(tokens[0])
  290. val := strings.TrimSpace(tokens[1])
  291. // If the name starts with a '!' then it should overwrite whatever we
  292. // currently have in the config already.
  293. priority := false
  294. if name[0] == '!' {
  295. priority = true
  296. name = name[1:]
  297. }
  298. switch name {
  299. case "RLOG_LOG_LEVEL":
  300. config.logLevel = updateIfNeeded(config.logLevel, val, priority)
  301. case "RLOG_TRACE_LEVEL":
  302. config.traceLevel = updateIfNeeded(config.traceLevel, val, priority)
  303. case "RLOG_TIME_FORMAT":
  304. config.logTimeFormat = updateIfNeeded(config.logTimeFormat, val, priority)
  305. case "RLOG_LOG_FILE":
  306. config.logFile = updateIfNeeded(config.logFile, val, priority)
  307. case "RLOG_LOG_STREAM":
  308. val = strings.ToUpper(val)
  309. config.logStream = updateIfNeeded(config.logStream, val, priority)
  310. case "RLOG_LOG_NOTIME":
  311. config.logNoTime = updateIfNeeded(config.logNoTime, val, priority)
  312. case "RLOG_CALLER_INFO":
  313. config.showCallerInfo = updateIfNeeded(config.showCallerInfo, val, priority)
  314. case "RLOG_GOROUTINE_ID":
  315. config.showGoroutineID = updateIfNeeded(config.showGoroutineID, val, priority)
  316. default:
  317. rlogIssue("Unknown or illegal setting name in config file %s:%d. Ignored.",
  318. settingConfFile, i)
  319. }
  320. }
  321. }
  322. // configFromEnv extracts settings for our logger from environment variables.
  323. func configFromEnv() rlogConfig {
  324. // Read the initial configuration from the environment variables
  325. return rlogConfig{
  326. logLevel: os.Getenv("RLOG_LOG_LEVEL"),
  327. traceLevel: os.Getenv("RLOG_TRACE_LEVEL"),
  328. logTimeFormat: os.Getenv("RLOG_TIME_FORMAT"),
  329. logFile: os.Getenv("RLOG_LOG_FILE"),
  330. confFile: os.Getenv("RLOG_CONF_FILE"),
  331. logStream: strings.ToUpper(os.Getenv("RLOG_LOG_STREAM")),
  332. logNoTime: os.Getenv("RLOG_LOG_NOTIME"),
  333. showCallerInfo: os.Getenv("RLOG_CALLER_INFO"),
  334. showGoroutineID: os.Getenv("RLOG_GOROUTINE_ID"),
  335. confCheckInterv: os.Getenv("RLOG_CONF_CHECK_INTERVAL"),
  336. }
  337. }
  338. // init loads configuration from the environment variables and the
  339. // configuration file when the module is imorted.
  340. func init() {
  341. UpdateEnv()
  342. }
  343. // getTimeFormat returns the time format we should use for time stamps in log
  344. // lines, or nothing if "no time logging" has been requested.
  345. func getTimeFormat(config rlogConfig) string {
  346. settingDateTimeFormat = ""
  347. logNoTime := isTrueBoolString(config.logNoTime)
  348. if !logNoTime {
  349. // Store the format string for date/time logging. Allowed values are
  350. // all the constants specified in
  351. // https://golang.org/src/time/format.go.
  352. var f string
  353. switch strings.ToUpper(config.logTimeFormat) {
  354. case "ANSIC":
  355. f = time.ANSIC
  356. case "UNIXDATE":
  357. f = time.UnixDate
  358. case "RUBYDATE":
  359. f = time.RubyDate
  360. case "RFC822":
  361. f = time.RFC822
  362. case "RFC822Z":
  363. f = time.RFC822Z
  364. case "RFC1123":
  365. f = time.RFC1123
  366. case "RFC1123Z":
  367. f = time.RFC1123Z
  368. case "RFC3339":
  369. f = time.RFC3339
  370. case "RFC3339NANO":
  371. f = time.RFC3339Nano
  372. case "KITCHEN":
  373. f = time.Kitchen
  374. default:
  375. if config.logTimeFormat != "" {
  376. f = config.logTimeFormat
  377. } else {
  378. f = time.RFC3339
  379. }
  380. }
  381. settingDateTimeFormat = f + " "
  382. }
  383. return settingDateTimeFormat
  384. }
  385. // initialize translates config items into initialized data structures,
  386. // config values and freshly created or opened config files, if necessary.
  387. // This function prepares everything for the fast and efficient processing of
  388. // the actual log functions.
  389. // Importantly, it takes the passed in configuration and combines it with any
  390. // configuration provided in a configuration file.
  391. // If the reInitEnvVars flag is set then the passed-in configuration overwrites
  392. // the settings stored from the environment variables, which we need for our tests.
  393. func initialize(config rlogConfig, reInitEnvVars bool) {
  394. var err error
  395. initMutex.Lock()
  396. defer initMutex.Unlock()
  397. if reInitEnvVars {
  398. configFromEnvVars = config
  399. }
  400. // Read and merge configuration from the config file
  401. updateConfigFromFile(&config)
  402. var checkTime int
  403. checkTime, err = strconv.Atoi(config.confCheckInterv)
  404. if err == nil {
  405. settingCheckInterval = time.Duration(checkTime) * time.Second
  406. } else {
  407. if config.confCheckInterv != "" {
  408. rlogIssue("Cannot parse config check interval value '%s'. Using default.",
  409. config.confCheckInterv)
  410. }
  411. }
  412. settingShowCallerInfo = isTrueBoolString(config.showCallerInfo)
  413. settingShowGoroutineID = isTrueBoolString(config.showGoroutineID)
  414. // initialize filters for trace (by default no trace output) and log levels
  415. // (by default INFO level).
  416. newTraceFilterSpec := new(filterSpec)
  417. newTraceFilterSpec.fromString(config.traceLevel, true, noTraceOutput)
  418. traceFilterSpec = newTraceFilterSpec
  419. newLogFilterSpec := new(filterSpec)
  420. newLogFilterSpec.fromString(config.logLevel, false, levelInfo)
  421. logFilterSpec = newLogFilterSpec
  422. // Evaluate the specified date/time format
  423. settingDateTimeFormat = getTimeFormat(config)
  424. // By default we log to stderr...
  425. // Evaluating whether a different log stream should be used.
  426. // By default (if flag is not set) we want to log date and time.
  427. // Note that in our log writers we disable date/time loggin, since we will
  428. // take care of producing this ourselves.
  429. if config.logStream == "STDOUT" {
  430. logWriterStream = log.New(os.Stdout, "", 0)
  431. } else if config.logStream == "NONE" {
  432. logWriterStream = nil
  433. } else {
  434. logWriterStream = log.New(os.Stderr, "", 0)
  435. }
  436. // ... but if requested we'll also create and/or append to a logfile
  437. var newLogFile *os.File
  438. if currentLogFileName != config.logFile { // something changed
  439. if config.logFile == "" {
  440. // no more log output to a file
  441. logWriterFile = nil
  442. } else {
  443. // Check if the logfile was changed or was set for the first
  444. // time. Only then do we need to open/create a new file.
  445. // We also do this if for some reason we don't have a log writer
  446. // yet.
  447. if currentLogFileName != config.logFile || logWriterFile == nil {
  448. newLogFile, err = os.OpenFile(config.logFile,
  449. os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
  450. if err == nil {
  451. logWriterFile = log.New(newLogFile, "", 0)
  452. } else {
  453. rlogIssue("Unable to open log file: %s", err)
  454. return
  455. }
  456. }
  457. }
  458. // Close the old logfile, since we are now writing to a new file
  459. if currentLogFileName != "" {
  460. currentLogFile.Close()
  461. currentLogFileName = config.logFile
  462. currentLogFile = newLogFile
  463. }
  464. }
  465. }
  466. // SetConfFile enables the programmatic setting of a new config file path.
  467. // Any config values specified in that file will be immediately applied.
  468. func SetConfFile(confFileName string) {
  469. configFromEnvVars.confFile = confFileName
  470. initialize(configFromEnvVars, false)
  471. }
  472. // UpdateEnv extracts settings for our logger from environment variables and
  473. // calls the actual initialization function with that configuration.
  474. func UpdateEnv() {
  475. // Get environment-based configuration
  476. config := configFromEnv()
  477. // Pass the environment variable config through to the next stage, which
  478. // produces an updated config based on config file values.
  479. initialize(config, true)
  480. }
  481. // SetOutput re-wires the log output to a new io.Writer. By default rlog
  482. // logs to os.Stderr, but this function can be used to direct the output
  483. // somewhere else. If output to two destinations was specified via environment
  484. // variables then this will change it back to just one output.
  485. func SetOutput(writer io.Writer) {
  486. // Use the stored date/time flag settings
  487. logWriterStream = log.New(writer, "", 0)
  488. logWriterFile = nil
  489. if currentLogFile != nil {
  490. currentLogFile.Close()
  491. currentLogFileName = ""
  492. }
  493. }
  494. // isTrueBoolString tests a string to see if it represents a 'true' value.
  495. // The ParseBool function unfortunately doesn't recognize 'y' or 'yes', which
  496. // is why we added that test here as well.
  497. func isTrueBoolString(str string) bool {
  498. str = strings.ToUpper(str)
  499. if str == "Y" || str == "YES" {
  500. return true
  501. }
  502. if isTrue, err := strconv.ParseBool(str); err == nil && isTrue {
  503. return true
  504. }
  505. return false
  506. }
  507. // rlogIssue is used by rlog itself to report issues or problems. This is mostly
  508. // independent of the standard logging settings, since a problem may have
  509. // occurred while trying to establish the standard settings. So, where can rlog
  510. // itself report any problems? For now, we just write those out to stderr.
  511. func rlogIssue(prefix string, a ...interface{}) {
  512. fmtStr := fmt.Sprintf("rlog - %s\n", prefix)
  513. fmt.Fprintf(os.Stderr, fmtStr, a...)
  514. }
  515. // basicLog is called by all the 'level' log functions.
  516. // It checks what is configured to be included in the log message, decorates it
  517. // accordingly and assembles the entire line. It then uses the standard log
  518. // package to finally output the message.
  519. func basicLog(logLevel int, traceLevel int, isLocked bool, format string, prefixAddition string, a ...interface{}) {
  520. now := time.Now()
  521. // In some cases the caller already got this lock for us
  522. if !isLocked {
  523. initMutex.RLock()
  524. defer initMutex.RUnlock()
  525. }
  526. // Check if it's time to load updated information from the config file
  527. if settingCheckInterval > 0 && now.Sub(lastConfigFileCheck) > settingCheckInterval {
  528. // This unlock always happens, since initMutex is locked at this point,
  529. // either by this function or the caller Initialize needs to be able to
  530. initMutex.RUnlock()
  531. // Get the full lock, so we need to release ours.
  532. initialize(configFromEnvVars, false)
  533. // Take our reader lock again. This is fine, since only the check
  534. // interval related items were read earlier.
  535. initMutex.RLock()
  536. }
  537. // Extract information about the caller of the log function, if requested.
  538. var callingFuncName string
  539. var moduleAndFileName string
  540. pc, fullFilePath, line, ok := runtime.Caller(2)
  541. if ok {
  542. callingFuncName = runtime.FuncForPC(pc).Name()
  543. // We only want to print or examine file and package name, so use the
  544. // last two elements of the full path. The path package deals with
  545. // different path formats on different systems, so we use that instead
  546. // of just string-split.
  547. dirPath, fileName := path.Split(fullFilePath)
  548. var moduleName string
  549. if dirPath != "" {
  550. dirPath = dirPath[:len(dirPath)-1]
  551. _, moduleName = path.Split(dirPath)
  552. }
  553. moduleAndFileName = moduleName + "/" + fileName
  554. }
  555. // Perform tests to see if we should log this message.
  556. var allowLog bool
  557. if traceLevel == notATrace {
  558. if logFilterSpec.matchfilters(moduleAndFileName, logLevel) {
  559. allowLog = true
  560. }
  561. } else {
  562. if traceFilterSpec.matchfilters(moduleAndFileName, traceLevel) {
  563. allowLog = true
  564. }
  565. }
  566. if !allowLog {
  567. return
  568. }
  569. callerInfo := ""
  570. if settingShowCallerInfo {
  571. if settingShowGoroutineID {
  572. callerInfo = fmt.Sprintf("[%d:%d %s:%d (%s)] ", os.Getpid(),
  573. getGID(), moduleAndFileName, line, callingFuncName)
  574. } else {
  575. callerInfo = fmt.Sprintf("[%d %s:%d (%s)] ", os.Getpid(),
  576. moduleAndFileName, line, callingFuncName)
  577. }
  578. }
  579. // Assemble the actual log line
  580. var msg string
  581. if format != "" {
  582. msg = fmt.Sprintf(format, a...)
  583. } else {
  584. msg = fmt.Sprintln(a...)
  585. }
  586. levelDecoration := levelStrings[logLevel] + prefixAddition
  587. logLine := fmt.Sprintf("%s%-9s: %s%s",
  588. now.Format(settingDateTimeFormat), levelDecoration, callerInfo, msg)
  589. if logWriterStream != nil {
  590. logWriterStream.Print(logLine)
  591. }
  592. if logWriterFile != nil {
  593. logWriterFile.Print(logLine)
  594. }
  595. }
  596. // getGID gets the current goroutine ID (algorithm from
  597. // https://blog.sgmansfield.com/2015/12/goroutine-ids/) by
  598. // unwinding the stack.
  599. func getGID() uint64 {
  600. b := make([]byte, 64)
  601. b = b[:runtime.Stack(b, false)]
  602. b = bytes.TrimPrefix(b, []byte("goroutine "))
  603. b = b[:bytes.IndexByte(b, ' ')]
  604. n, _ := strconv.ParseUint(string(b), 10, 64)
  605. return n
  606. }
  607. // Trace is for low level tracing of activities. It takes an additional 'level'
  608. // parameter. The RLOG_TRACE_LEVEL variable is used to determine which levels
  609. // of trace message are output: Every message with a level lower or equal to
  610. // what is specified in RLOG_TRACE_LEVEL. If RLOG_TRACE_LEVEL is not defined at
  611. // all then no trace messages are printed.
  612. func Trace(traceLevel int, a ...interface{}) {
  613. // There are possibly many trace messages. If trace logging isn't enabled
  614. // then we want to get out of here as quickly as possible.
  615. initMutex.RLock()
  616. defer initMutex.RUnlock()
  617. if len(traceFilterSpec.filters) > 0 {
  618. prefixAddition := fmt.Sprintf("(%d)", traceLevel)
  619. basicLog(levelTrace, traceLevel, true, "", prefixAddition, a...)
  620. }
  621. }
  622. // Tracef prints trace messages, with formatting.
  623. func Tracef(traceLevel int, format string, a ...interface{}) {
  624. // There are possibly many trace messages. If trace logging isn't enabled
  625. // then we want to get out of here as quickly as possible.
  626. initMutex.RLock()
  627. defer initMutex.RUnlock()
  628. if len(traceFilterSpec.filters) > 0 {
  629. prefixAddition := fmt.Sprintf("(%d)", traceLevel)
  630. basicLog(levelTrace, traceLevel, true, format, prefixAddition, a...)
  631. }
  632. }
  633. // Debug prints a message if RLOG_LEVEL is set to DEBUG.
  634. func Debug(a ...interface{}) {
  635. basicLog(levelDebug, notATrace, false, "", "", a...)
  636. }
  637. // Debugf prints a message if RLOG_LEVEL is set to DEBUG, with formatting.
  638. func Debugf(format string, a ...interface{}) {
  639. basicLog(levelDebug, notATrace, false, format, "", a...)
  640. }
  641. // Info prints a message if RLOG_LEVEL is set to INFO or lower.
  642. func Info(a ...interface{}) {
  643. basicLog(levelInfo, notATrace, false, "", "", a...)
  644. }
  645. // Infof prints a message if RLOG_LEVEL is set to INFO or lower, with
  646. // formatting.
  647. func Infof(format string, a ...interface{}) {
  648. basicLog(levelInfo, notATrace, false, format, "", a...)
  649. }
  650. // Println prints a message if RLOG_LEVEL is set to INFO or lower.
  651. // Println shouldn't be used except for backward compatibility
  652. // with standard log package, directly using Info is preferred way.
  653. func Println(a ...interface{}) {
  654. basicLog(levelInfo, notATrace, false, "", "", a...)
  655. }
  656. // Printf prints a message if RLOG_LEVEL is set to INFO or lower, with
  657. // formatting.
  658. // Printf shouldn't be used except for backward compatibility
  659. // with standard log package, directly using Infof is preferred way.
  660. func Printf(format string, a ...interface{}) {
  661. basicLog(levelInfo, notATrace, false, format, "", a...)
  662. }
  663. // Warn prints a message if RLOG_LEVEL is set to WARN or lower.
  664. func Warn(a ...interface{}) {
  665. basicLog(levelWarn, notATrace, false, "", "", a...)
  666. }
  667. // Warnf prints a message if RLOG_LEVEL is set to WARN or lower, with
  668. // formatting.
  669. func Warnf(format string, a ...interface{}) {
  670. basicLog(levelWarn, notATrace, false, format, "", a...)
  671. }
  672. // Error prints a message if RLOG_LEVEL is set to ERROR or lower.
  673. func Error(a ...interface{}) {
  674. basicLog(levelErr, notATrace, false, "", "", a...)
  675. }
  676. // Errorf prints a message if RLOG_LEVEL is set to ERROR or lower, with
  677. // formatting.
  678. func Errorf(format string, a ...interface{}) {
  679. basicLog(levelErr, notATrace, false, format, "", a...)
  680. }
  681. // Critical prints a message if RLOG_LEVEL is set to CRITICAL or lower.
  682. func Critical(a ...interface{}) {
  683. basicLog(levelCrit, notATrace, false, "", "", a...)
  684. }
  685. // Criticalf prints a message if RLOG_LEVEL is set to CRITICAL or lower, with
  686. // formatting.
  687. func Criticalf(format string, a ...interface{}) {
  688. basicLog(levelCrit, notATrace, false, format, "", a...)
  689. }