Pergunta

Aqui está um cenário. Você tem uma grande quantidade de scripts de legados, todos usando uma biblioteca comum. Disse scripts de usar a declaração 'print' para saída de diagnóstico. Não são permitidas alterações aos scripts -. Eles são muito diversificadas, têm suas aprovações, e há muito que deixaram os vales fecundos de supervisão e controle

Agora, uma nova necessidade chegou: o registo deve agora ser adicionados à biblioteca. Isso deve ser feito de forma automática e transparente, sem que os usuários da biblioteca padrão que precisam mudar seus scripts. métodos biblioteca comum pode simplesmente ter o registo de chamadas adicionados a eles; essa é a parte fácil. As mentiras parte mais difícil no fato de que a saída do diagnóstico a partir destes scripts foram sempre exibidos usando a instrução 'print'. Esta saída de diagnóstico devem ser armazenados, mas apenas como importante, processado.

Como um exemplo dessa transformação, a biblioteca só deve gravar as linhas impressas que contenham as palavras 'aviso', 'erro', 'aviso', ou 'atenção'. O abaixo Extremamente Trivial e artificial Código Exemplo (tm) iria gravar alguns dos referidos saída:

sub CheckPrintOutput
{
    my @output = @_; # args passed to print eventually find their way here.
    foreach my $value (@output) {
         Log->log($value) if $value =~ /warning|error|notice|attention/i;
    }
}

(eu gostaria de evitar problemas como 'o que deve realmente estar conectado', 'impressão não deve ser usado para diagnóstico', 'perl suga', ou 'neste exemplo tem as falhas xy e z' .. .Esta é bastante simplificado para concisão e clareza.)

O problema básico se resume a capturar e processar dados passados ??para imprimir (ou qualquer builtin perl, ao longo destas linhas de raciocínio). É possível? Existe alguma maneira de fazê-lo de forma limpa? Existem quaisquer módulos de registo que têm ganchos para deixar você fazer isso? Ou é algo que deve ser evitado como a peste, e eu deveria desistir nunca capturar e processar a saída impressa?

adicionais: Este deve executar multi-plataforma - Windows e * nix iguais. O processo de execução dos scripts deve permanecer a mesma, como obrigação a saída do script.

adicional adicionais: Uma sugestão interessante feita nos comentários da resposta de codelogic:

Você pode subclasse http://perldoc.perl.org/IO/Handle.html e criar seu identificador de arquivo próprio que irá fazer o trabalho de registro. - Kamil Kisiel

Isto pode fazê-lo, com duas ressalvas:

1) Eu preciso de uma maneira de exportar essa funcionalidade para qualquer um que usa a biblioteca comum. Ele teria de aplicar automaticamente para STDOUT e, provavelmente, STDERR também.

2) a documentação IO :: Handle diz que você não pode subclasse-lo e minhas tentativas até agora têm sido infrutíferos. Existe especial qualquer coisa necessária para fazer sublclassing trabalho IO :: Handle? O padrão 'de uso base 'IO :: Handle' e, em seguida, substituindo os novos métodos / impressão parecem não fazer nada.

edição final: Parece que IO :: Handle é um beco sem saída, mas Tie :: Handle pode fazê-lo. Obrigado por todas as sugestões; todos eles são realmente bom. Eu vou dar o Tie :: rota Handle uma tentativa. Se ele causa problemas eu vou estar de volta!

Adenda: Note que depois de trabalhar com isso um pouco, descobri que Tie :: Handle vai funcionar, se você não fizer nada complicado. Se você usar qualquer um dos recursos de IO :: Handle com o seu amarrado STDOUT ou STDERR, é basicamente um jogo de dados para obtê-los a trabalhar de forma confiável - Eu não poderia encontrar uma maneira de obter o método autoflush de IO :: Handle para trabalhar no meu amarrado lidar com. Se eu habilitado autoflush antes de eu amarrado a alça que ele iria trabalhar. Se isso funciona para você, a rota Tie :: Handle pode ser aceitável.

Foi útil?

Solução

Há uma série de built-ins que você pode substituir (ver perlsub ). No entanto, print é um dos built-ins que não funciona dessa maneira. As dificuldades de substituir print são detalhados neste de perlmonk fio .

No entanto, você pode

  1. Criar um pacote
  2. Tie uma alça
  3. Selecione esta alça.

Agora, um par de pessoas deram a estrutura básica, mas ele funciona mais ou menos como esta:

package IO::Override;
use base qw<Tie::Handle>;
use Symbol qw<geniosym>;

sub TIEHANDLE { return bless geniosym, __PACKAGE__ }

sub PRINT { 
    shift;
    # You can do pretty much anything you want here. 
    # And it's printing to what was STDOUT at the start.
    # 
    print $OLD_STDOUT join( '', 'NOTICE: ', @_ );
}

tie *PRINTOUT, 'IO::Override';
our $OLD_STDOUT = select( *PRINTOUT );

Você pode substituir printf da mesma maneira:

sub PRINTF { 
    shift;
    # You can do pretty much anything you want here. 
    # And it's printing to what was STDOUT at the start.
    # 
    my $format = shift;
    print $OLD_STDOUT join( '', 'NOTICE: ', sprintf( $format, @_ ));
}

Tie :: Handle para tudo o que você pode substituir o comportamento de STDOUT .

Outras dicas

Você pode usar do Perl selecionar para redirecionar STDOUT.

open my $fh, ">log.txt";
print "test1\n";
my $current_fh = select $fh;
print "test2\n";
select $current_fh;
print "test3\n";

O identificador de arquivo pode ser qualquer coisa, até mesmo um tubo para outro processo que pós processa as suas mensagens de log.

Perlío :: tee em Perlío :: Util módulo parece permite 'tee' do saída de um identificador de arquivo para vários destinos (eg processador log e stdout).

Muitas opções. Use select () para alterar o filehandle que os padrões de impressão para. Ou amarre STDOUT. Ou reabri-lo. Ou aplicar uma camada de IO a ele.

Esta não é a resposta para o seu problema, mas você deve ser capaz de adotar a lógica para o seu próprio uso. Se não, talvez alguém vai encontrá-lo útil.

Catching cabeçalhos malformados antes que eles aconteçam ...

package PsychicSTDOUT;
use strict;

my $c = 0;
my $malformed_header = 0;
open(TRUE_STDOUT, '>', '/dev/stdout');
tie *STDOUT, __PACKAGE__, (*STDOUT);

sub TIEHANDLE {
    my $class = shift;
    my $handles = [@_];
    bless $handles, $class;
    return $handles;
}

sub PRINT {
    my $class = shift;
    if (!$c++ && @_[0] !~ /^content-type/i) {
        my (undef, $file, $line) = caller;
        print STDERR "Missing content-type in $file at line $line!!\n";
        $malformed_header = 1;
    }
    return 0 if ($malformed_header);
    return print TRUE_STDOUT @_;
}
1;

uso:

use PsychicSTDOUT;
print "content-type: text/html\n\n"; #try commenting out this line
print "<html>\n";
print "</html>\n";

Você pode executar o script a partir de um script que stdout e captura do roteiro original escreve a saída em algum lugar sensível.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top