2010-02-10 21 views
26

Tôi có một máy chủ web nhỏ mà tôi đã viết với Sinatra. Tôi muốn có thể đăng nhập tin nhắn vào một tệp nhật ký. Tôi đã đọc qua http://www.sinatrarb.com/api/index.html và www.sinatrarb.com/intro.html và tôi thấy rằng Rack có tên Rack :: CommonLogger, nhưng tôi không thể tìm thấy bất kỳ ví dụ nào về cách nó có thể được truy cập và sử dụng để ghi nhật ký. Ứng dụng của tôi rất đơn giản vì vậy tôi đã viết nó như là một DSL cấp cao nhất, nhưng tôi có thể chuyển sang phân lớp nó từ SinatraBase nếu đó là một phần của những gì được yêu cầu.Sử dụng Rack :: CommonLogger in Sinatra

Trả lời

41

Rack::CommonLogger sẽ không cung cấp nhật ký cho ứng dụng chính của bạn, nó sẽ chỉ ghi lại yêu cầu như Apache sẽ thực hiện.

Kiểm tra mã của mình: https://github.com/rack/rack/blob/master/lib/rack/common_logger.rb

Tất cả Rack ứng dụng có gọi phương thức mà có được ấy gọi với HTTP Request env, nếu bạn đánh dấu vào phương pháp gọi của middleware này đây là những gì sẽ xảy ra:

def call(env) 
    began_at = Time.now 
    status, header, body = @app.call(env) 
    header = Utils::HeaderHash.new(header) 
    log(env, status, header, began_at) 
    [status, header, body] 
end 

@app Trong trường hợp này là ứng dụng chính, phần mềm trung gian chỉ đăng ký thời gian yêu cầu bắt đầu, sau đó lớp trung gian của bạn nhận được [trạng thái, tiêu đề, nội dung] ba và sau đó gọi phương thức nhật ký riêng với các tham số đó , trả lại cùng một gấp ba lần Trang được trả lại ngay từ đầu.

Phương pháp logger đi như thế:

def log(env, status, header, began_at) 
    now = Time.now 
    length = extract_content_length(header) 

    logger = @logger || env['rack.errors'] 
    logger.write FORMAT % [ 
    env['HTTP_X_FORWARDED_FOR'] || env["REMOTE_ADDR"] || "-", 
    env["REMOTE_USER"] || "-", 
    now.strftime("%d/%b/%Y %H:%M:%S"), 
    env["REQUEST_METHOD"], 
    env["PATH_INFO"], 
    env["QUERY_STRING"].empty? ? "" : "?"+env["QUERY_STRING"], 
    env["HTTP_VERSION"], 
    status.to_s[0..3], 
    length, 
    now - began_at ] 
end 

Như bạn có thể nói, phương pháp log chỉ lấy một số thông tin từ env yêu cầu, và đăng nhập vào trên một logger được chỉ định vào cuộc gọi constructor, nếu có không dụ logger sau đó nó đi vào rack.errors logger (có vẻ như có một mặc định)

cách sử dụng nó (trong bạn config.ru):

logger = Logger.new('log/app.log') 

use Rack::CommonLogger, logger 
run YourApp 

Nếu bạn muốn có một logger phổ biến ở tất cả các ứng dụng của bạn, bạn có thể tạo một middleware logger đơn giản:

class MyLoggerMiddleware 

    def initialize(app, logger) 
    @app, @logger = app, logger 
    end 

    def call(env) 
    env['mylogger'] = @logger 
    @app.call(env) 
    end 

end 

Để sử dụng nó, trên config.ru của bạn:

logger = Logger.new('log/app.log') 
use Rack::CommonLogger, logger 
use MyLoggerMiddleware, logger 
run MyApp 

Hope this helps.

+4

Không phải dòng đầu tiên của cuộc gọi MyLoggerMiddleware # (env) là: env ['rack.errors'] = @logger ? –

+0

Ngoài ra, tôi không muốn đăng nhập mọi yêu cầu, chỉ cảnh báo và thông báo lỗi. Nhưng tôi muốn nó được cấu hình để tôi có thể thiết lập mức độ đăng nhập, như trong "gỡ rối", "thông tin", "cảnh báo", "lỗi", ... BTW - Ứng dụng của tôi không phải là một Rails ứng dụng. Không có tệp config.ru. Đó là một ứng dụng Sinatra đơn giản. Tôi đã hy vọng sử dụng một tiêu chuẩn hiện có, nhưng không thể hiểu được đó là gì. Có lẽ tôi phải lấy CommonLogger bạn cho tôi xem và tự mình làm một mình? –

+0

'config.ru' là tệp cấu hình cho Rack, không phải cho Rails. Cả Sinatra và Rails đều dựa trên Rack, vì vậy bạn có thể sử dụng 'config.ru' trong các ứng dụng Sinatra. – jafrog

2

Tôi đi theo những gì tôi tìm thấy trên blog bài này - trích dưới đây

require 'rubygems' 
require 'sinatra' 

disable :run 
set :env, :production 
set :raise_errors, true 
set :views, File.dirname(__FILE__) + '/views' 
set :public, File.dirname(__FILE__) + '/public' 
set :app_file, __FILE__ 

log = File.new("log/sinatra.log", "a") 
STDOUT.reopen(log) 
STDERR.reopen(log) 

require 'app' 
run Sinatra.application 

sau đó sử dụng puts hoặc print. Nó làm việc cho tôi.

+1

đó làm việc, nhưng tôi thực sự muốn tìm hiểu làm thế nào để sử dụng rack :: CommonLogger để gửi tin nhắn được định dạng với thời gian. –

15

Trong config.ru của bạn:

root = ::File.dirname(__FILE__) 
logfile = ::File.join(root,'logs','requests.log') 

require 'logger' 
class ::Logger; alias_method :write, :<<; end 
logger = ::Logger.new(logfile,'weekly') 

use Rack::CommonLogger, logger 

require ::File.join(root,'myapp') 
run MySinatraApp.new # Subclassed from Sinatra::Application 
+0

tôi thử giải pháp của bạn, và nó hoạt động. tôi tự hỏi tại sao nó phải ở trong config.ru? – Chamnap

+0

một tập tin những điều tôi không 100% trên đó, bạn sẽ nhớ bình luận về những gì/lý do tại sao một số dòng là có xin vui lòng? –

1
class ErrorLogger 
    def initialize(file) 
    @file = ::File.new(file, "a+") 
    @file.sync = true 
    end 

    def puts(msg) 
    @file.puts 
    @file.write("-- ERROR -- #{Time.now.strftime("%d %b %Y %H:%M:%S %z")}: ") 
    @file.puts(msg) 
    end 
end 


class App < Sinatra::Base 

    if production? 
    error_logger = ErrorLogger.new('log/error.log') 

    before { 
     env["rack.errors"] = error_logger 
    } 
    end 

    ... 

end 
0

Mở lại STDOUT và chuyển hướng nó vào một tập tin không phải là một ý tưởng tốt nếu bạn sử dụng hành khách. Nó gây ra trong trường hợp của tôi rằng Hành khách không bắt đầu. Đọc https://github.com/phusion/passenger/wiki/Debugging-application-startup-problems#stdout-redirection cho vấn đề này.

Đây sẽ là một cách đúng đắn thay vì:

logger = ::File.open('log/sinatra.log', 'a+') 
Sinatra::Application.use Rack::CommonLogger, logger 
Các vấn đề liên quan