使用Racket生成日志信息

时间:2013-09-13 15:27:31

标签: macros racket

背景

我打算为我正在开发的代码生成调试消息。我编写了一个宏来避免在每个函数中编写日志记录调用。我知道这限制了生成更多自定义调试消息的能力,但作为回报,它将日志记录与代码隔离开来。这就是我的目标。这种宏方法也有其他缺点,例如它只限制了对这个宏的函数绑定的创建,但我想我可以忍受它。

以下是宏的定义和演示其用法的示例。

(define-syntax (define-func stx)
  (syntax-case stx ()
    [(define-func (func-name args ...) body1 body2 ...)
     (if (and (identifier? #'func-name)
              (andmap symbol? (syntax->datum #'(args ...))))
       (syntax (define (func-name args ...)
                 (log-debug (format "Function-name ~a:" (syntax-e #'func-name)) (list args ...))
                 body1
                 body2 ...))
       (raise-syntax-error 'define-func "not an identifier" stx))]
    [else (raise-syntax-error 'define-func "bad syntax" stx)]))



(define-func (last l)
  (cond [(null? l) null]
        [(null? (rest l)) (first l)]
        [else (last (rest l))]))


(define-func (main)
  (last (list 1 2 3 4 5 6 7 8 9))
  (logger))

log-debug和logger在单独的模块中定义

产生的输出有点像:

Function-name last: 
args: 
:-> (7 8 9) 


Function-name last: 
args: 
:-> (8 9) 


Function-name last: 
args: 
:-> (9) 

现在我想让它更具可读性。可读性我的意思是提供某种缩进,以便阅读日志的人可以理解呼叫流程。例如以下内容:

Function-name last: 
args: 
:-> (7 8 9) 

    Function-name last: 
    args: 
    :-> (8 9) 

        Function-name last: 
        args: 
        :-> (9) 

更容易找出谁叫谁等等。我有一个想法可以做到这一点。它涉及一个跟踪缩进的变量,然后在记录函数名称之后我将增加缩进,并且在评估body之后并且在返回之前值减少该值。如下所示:

(define indent 0)

(define-syntax (define-func stx)
  (syntax-case stx ()
    [ (... ...)
      (...
      (log-debug ...)
      (increment indent)
      (let [(retval (body1 body2 ...)]
        (decrease indent)
        retval))]))

增量和减少分别增加和减少压痕。

问题

它甚至可以用于返回void的函数。我不确定它是否是正确的行为。在球拍中,void是一个特殊的值,但我不确定创建一个与void的绑定是正确的方法。

有没有更好的方法来实现同样的目标?如果没有,这个设计有什么问题吗?只要他们将日志记录和代码分开,我就会接受任何想法/改变。

感谢您的帮助!

1 个答案:

答案 0 :(得分:4)

我有几个建议:

  • 最好使用parameter代替变量,用于缩进级别等“全局”内容,因为在parameterize表达式的末尾会为您恢复原始值。 / LI>
  • 您在宏中进行的所有raise-syntax-error检查都是多余的:syntax-case已经提供了警卫(也称为挡泥板),允许您对必要的宏“参数”进行任何验证:

    (define-syntax (define-func stx)
      (syntax-case stx ()
        [(_ (func-name args ...) body1 body2 ...)
         (andmap identifier? (syntax->list #'(func-name args ...)))
         #'(define (func-name args ...)
             (log-debug (format "Function-name ~a:" 'func-name)
                        (list args ...))
             body1
             body2 ...)]))
    

    我已经在几个地方修改了你的代码,如上所示:

    1. 我使用(_ ...)代替(define-func ...),因为在syntax-case中(与syntax-rules不同),后者实际上会绑定一个名为define-func的模式变量,会影响你可能想做的任何递归宏调用(我会批准你在这里没有一个,但无论如何这都是一个好习惯。)
    2. 我没有完全展平守卫中的#'(args ...),而是将其转换为语法对象列表,以便您可以使用identifier?进行测试。与使用symbol?进行测试相比,这更有意图揭示,并允许我们在同一个表达式中测试func-name
    3. 您无需在扩展代码中使用(syntax-e #'func-name)!请引用它。