Pillole Laravel: errore HTTP 500 accedendo ad una applicazione

Errore 500 e questo messaggio di log:

PHP Parse error:  syntax error, unexpected '?' in /vendor/symfony/http-foundation/Response.php

Come trovato qui, si risolve togliendo php7.0 (attualmente sto girando con php7.2)

$ sudo apt-get purge php7.0 php7.0-common ; sudo apache2ctl restart

L’errore sparisce.

Pillole Laravel: come evitare di creare implicitamente due colonne chiave sulla stessa tabella

Lo strumento delle migrazioni di Laravel è molto comodo per definire le tabelle di uno schema ma anche per le conseguenti ricadute positive sull’ORM Eloquent ai fini di produrre query con le join già fatte.

Mi riferisco al fatto che è possibile definire i vincoli di chiave esterna (foreign key). Solo che la definizione è un po’ “tricky”. Prendiamo questo esempio: due tabelle customers e orders con una relazione di chiave esterna da customers a orders:

Definizione della tabella customers, un solo attributo chiave, implicitamente definito con increments()
C’è un attributo di riferimento a customer_id

Se faccio girare lo script di migrazione, che si incarica di creare gli oggetti nello schema del DB ottengo un errore:

Illuminate\Database\QueryException  : SQLSTATE[42000]: Syntax error or access violation: 1075 Incorrect table definition; there can be only one auto column and it must be defined as a key

L’errore è abbstanza grave: abbiamo tentato di creare due chiavi: la prima con il campo id la seconda con il campo customer_id. 

All’inizio credevo che fosse un problema della definizione del constraint, e così l’ho scorporato in una seconda invocazione separata: Schema::table eccetera. Invece il problema era com’è stato definito il campo, infatti ho lo stesso errore anche togliendo la definizione del contraint. Il problema è la dimensione del campo:

$table->unsignedInteger('customer_id', 10);

Ora lo spiego qui e sembra ovvio, ma trovarlo, come tutti i buoni martorei, richiede tempo: il metodo $table->unsignedInteger() ha un secondo parametro che non è affatto la dimensione del campo, che lui si regola automaticamente, ma è – disgraziatamente…

 public function unsignedInteger($column, $autoIncrement = false)
    {
        return $this->integer($column, $autoIncrement, true);
    }

… l’informazione $autoIncrement. Essendo disgrazatamente 10 == true in PHP, il campo così viene dichiarato come autoincrement e quindi come chiave primaria generando l’errore. Invece con questa dichiarazione:

La migrazione termina correttamente

In origine, in ogni caso, c’era la mia errata invocazione del metodo.

[Hint: http://bit.ly/2zS9w1v]

Nobel per la Fisica 2018 a lavori sulla tecnologia laser

https://www.nobelprize.org

Il premio Nobel per la Fisica 2018 è stato assegnato a lavori sulle pinze laser, – ad Arthur Ashkin, una tecnologia che consente di spostare in modo estremamente preciso oggetti microscopici come i virus utilizzando la pressione elettromagnetica della luce – e sugli impulsi ultrabrevi (Mourou e Strickland). Con questa tecnica si ottengono impulsi della durata di un femtosecondo (10-15 secondi).

Per avere una idea di quanto breve sia il femtosecondo si pensi che in 1 secondo ci stanno tanti fs quanto il numero di secondi nell’età dell’universo (circa 1018):

1 s : 1 fs = Età dell’universo : 1 s

Il laser a femtosecondo, così chiamato in breve, viene utilizzato nella chirurgia dell’occhio, per la sua capacità di dosare in modo precisissimo l’energia degli impulsi.

Pillole Linux: riesumare una finestra “fantasma”

Oggi mi sono trovato davanti un problema sconosciuto: ho aperto una finestra (nella fattispecie, l’applet Java di MirthConnect) e ho poi normalmente commutato su un’altra finestra da quel momento non vedevo più la finestra Java nell’elenco dei task che vengono visualizzati con ALT-TAB:

La finestra in foreground non c’è nell’elenco dei task.

Se minimizzo la finestra di Mirthconnect non riesco più a recuperarla… Come posso fare ad accedervi nuovamente? il Linux ci sono due bei strumenti:

  • wmctrl interact with a EWMH/NetWM compatible X Window Manager: consente di fare cose meravigliose per automatizzare il desktop tramite script bash, ma in questo caso voglio solo vedere l’elenco delle finestre attive:
$ wmctrl -l
0x05800008 0 jsbach Aggiornamenti software
0x03000002 0 jsbach XdndCollectionWindowImp
0x03000003 0 jsbach unity-launcher
0x03000004 0 jsbach unity-panel
0x03000005 0 jsbach unity-dash
0x03000006 0 jsbach Hud
0x0340000a 0 jsbach Scrivania
0x03402081 0 jsbach dati_effettivi
0x00e00001 0 jsbach command line - Maximize an application with known PID from CLI - Ask Ubuntu - Google Chrome
0x05200084 0 jsbach *analisi_errori.txt (~/Documenti/clienti/XYZ/anomalie/180927) - gedit
0x04e000b5 0 jsbach prod36.mirth.local - Mirth Connect Administrator - (3.5.2)
0x03600004 0 jsbach dati_180927.xml - /home/marcob/Documenti/clienti/XYZ/2018/ - Geany
0x05a0000a 0 jsbach Terminale
0x04c00007 0 jsbach Database - KeePassX
0x05e000b0 0 jsbach prod35.mirth.local - Mirth Connect Administrator - (3.5.2)

Come si può notare le finestre attive sono più di quelle mostrate con ALT-TAB; in particolare ho evidenziato le due finestra scomparse: mi serve accedere ad una di queste due (l’ultima)

C’è poi un secondo tool molto potente:

  • xdotool fake keyboard/mouse input, window management, and more:
    questa utility simula l’uso della tastiera e di movimenti del mouse prodotti con uno script bash! Nel mio caso mi serve solo per sapere quale finestra è associata al processo che vedo essere quello sotto il quale gira il client Mirtchconnect
$ ps ax | grep java
4201 ? Sl 6:24 /usr/local/java/jre1.8.0_73/bin/java -eccetera
6102 ? Sl 6:24 /usr/local/java/jre1.8.0_73/bin/java -eccetera

Quello che mi serve è agganciare è il processo 6102. Per vedere quale finestra è associata eseguo:

$ xdotool search --pid 6102 | tail -1
98566320

xdotool mi dice che il processo 6102 è legato alla finestra 98566320, in esadecimale 0x05e000b0.

Ora xdotool permette di ripristinare la finestra come se la selezionassimo con ALT-TAB!

$ xdotool windowactivate 5e000b0

Si può fare anche tutto con un solo comando

$ xdotool windowactivate xdotool search --pid 6102 | tail -1

L’effetto è quello di riportare in foreground la finestra fantasma e ricominciarci a lavorare. Con la linea di comando 🙂

La finestra “fantasma” è nuovamente disponibile.

Pillole protocols: WebDAV

Il web come lo conosciamo oggi è un mezzo per lo più in sola lettura.

È vero che con il Web 2.0 anche gli utenti contribuiscono ai contenuti web, ma ciò avviene in modo molto diverso e molto più sofisticato rispetto a quanto immaginato inizialmente da Tim Berners-Lee.

Il suo browser WWW infatti agiva sui server web in lettura e scrittura: si poteva leggere una pagina web ma si poteva anche modificarla.

Come si “scrive” sul web ai tempi del Web 2.0

Quello che accade attualmente con il Web 2.0, ad esempio quando aggiungiamo un commento ad un articolo di un blog WordPress, è qualcosa di più complicato, ossia la scrittura in un database e non nel filesystem del server che eroga le pagine Web.

Tecnicamente quando invochiamo la funzionalità di aggiunta di un commento, invochiamo un comando di lettura di una risorsa web (comando HTTP POST) che si chiama wp-comments-post.php passando dei parametri:

POST /wp-comments-post.php HTTP/1.1 
Host: www.betaingegneria.it 
Connection: keep-alive 
Content-Length: 184 
...

comment=questo %22 il mio commento&author=My+Name&email=my.mail%40gmail.com&url=&submit=Submit+Comment&comment_post_ID=2734&comment_parent=0&akismet_comment_nonce=afb00fa731&ak_js=1535630177692

Qui, disgraziatamente, la parola post è utilizzata con due significati diversi:

  • POST come comando HTTP (nel senso di INVIA)
  • POST come articolo del blog

In questa operazione di scrittura del commento, il browser chiede (in lettura!) al server di caricare il programma wp-comments-post.php. Questa richiesta è inviata al server quando nella pagina del post clicchiamo sul link “Submit Comment” (o “Invia Commento” o qualcos’altro), assieme alla mail e il valore corrispondente alla scelta I’m not a ROBOT. Con il comando POST i parametri vengono inviati dopo il termine dell’intestazione HTTP e un doppio a capo (come illustrato sopra). Se utilizzassimo il comando di HTTP GET i parametri sarebbero contenuti nell’URL, separando il nome della risorsa dai parametri con un “?”:

GET /wp-comments-post.php?comment=questo %22 il mio commento&author=My+Name&email=my.mail%40gmail.com&url=&submit=Submit+Comment&comment_post_ID=2734&comment_parent=0&akismet_comment_nonce=afb00fa731&ak_js=1535630177692 HTTP/1.1 

Al di là di questa differenza tra GET e POST è importante capire bene qui che la risorsa (nome tecnico della “pagina” PHP)  wp-comments-post.php, non viene alterata, viene soltanto letta!

Quello che succede a questo punto è che il server web Apache non serve direttamente la pagina PHP al browser, anzi passa il controllo all’application server (il PHP nel caso di WordPress) il quale legge ed esegue il programma PHP wp-comments-post.php. All’interno di questo programma ci sono le istruzioni che salvano il nostro commento (“questo è il mio commento”) in un database. Il tutto è illustrato nella figura 1.

Successivamente, se tutto va bene, (in modo automatico) il server risponderà al browser inoltrandogli una sola intestazione (quindi non una pagina web) contenente soltanto un comando di redirect, ovvero una ingiunzione al browser di effettuare un’altra richiesta al web server perché gli serva il post che ora conterrà anche il nostro commento (vedi fig. 2).

Qui ci si soffermi a capire bene: è sempre il browser che inizia la richiesta di un contenuto. Egli agisce sempre come un cliente.

Tutto questo giro è perché la pagina HTML (il post contenente il commento) che viene consegnata dal web server al browser in realtà è il frutto di elaborazione del server applicativo PHP che legge il contenuto dell’articolo (anch’esso contenuto nel database) e gli eventuali commenti e compila un’unica pagina HTML che consegnerà ad Apache il quale la spedirà al nostro browser.

Possiamo immaginare il web server Apache, in questa architettura, semplicemente come un passa-mano:

  • in andata (REQUEST) riceve i parametri dal browser e li passa al server PHP
  • al ritorno (RESPONSE) riceve dal programma PHP il flusso HTML e la passa al nostro browser

Quindi sostanzialmente stiamo sempre facendo operazioni di lettura nel file system; le scritture vengono fatte su un DBMS.

Leggere e scrivere fisicamente sul Web

Ciò che aveva in mente Tim Berners-Lee, e che realizzò con il suo browser WWW, era una cosa profondamente diversa: le stesse risorse web dovevano essere modificabili. Quindi gli stessi file HTML contenuti nel web server! Ma questo ha un senso per un web statico in cui le pagine vengono servite al browser così come esistono nel web server. Nel cosiddetto “web dinamico” che esiste fin dalla comparsa degli script cgi-bin (quindi primi anni ’90, quando il web andava all’asilo) ciò non è più vero: non esistono pagine da servire così come sono, esistono solo programmi che producono pagine al volo sulla base dei dati di ingresso.

Quindi non ha nemmeno tanto senso il pensare di modificare le pagine, anzi: è da evitare nel modo più assoluto.

Questo approccio ha un senso quando si vuole utilizzare una infrastruttura web per pubblicare documenti statici che, tuttavia, hanno un loro ciclo di vita, possono cioè cambiare nel tempo ma in seguito ad azione di editing, di modifica da parte di uno o più utenti che sono quindi editori, gestori del contenuto di quella pagina. In quest’ottica si colloca il protocollo WebDAV che è una estensione di HTTP per questo tipo di attività.

Dunque il protocollo WebDAV realizza quanto aveva in mente Tim Berners-Lee. Ma generalmente si utilizza nelle intranet e spesso non si fa nemmeno utilizzo del browser per accedere ai contenuti, ma si impiega piuttosto un programma del tipo Finder o Esplora Risorse. La gestione dei documenti va oltre ai soli documenti HTML ma può trattarsi anche di file di testo elettronico come Word (MS Office) o Writer (LibreOffice) o PDF. La loro modifica è operata Offline, ossia modifico il documento in locale e poi lo deposito nel server andando ad alimentare la storia del documento con una nuova versione.

Il protocollo WebDAV è descritto nella RFC 4918 “HTTP Extensions for Web Distributed Authoring and Versioning (WebDAV)” ed è, al pari di tutti gli altri protocolli, essenzialmente un insieme di comandi. Con questi comandi possiamo non solo trasferire i file  da client a server, ma anche

  • creare directory (MKCOL)
  • copiare e spostare file (COPY / MOVE)
  • aggiungere, modificare e rimuovere proprietà (metadati) del file (PROPFIND, PROPPATCH)
  • bloccare e sbloccare (LOCK/UNLOCK) una risorsa quando si deve operare una modifica (condizione per una gestione transazionale della vita del documento – in altre parole evitiamo che più utenti mettano mano simultaneamente al documento)
  • cercare file (SEARCH)

Per allestire un server WebDAV occorre installare nel file server, prima di tutto, un server Web, ad esempio Apache. Poi occorre aggiungere il modulo DAV (mod_dav). Poiché i metodi di acesso DAV consentono ai client remoti di manipolare i file sul server, è necessario avere particolare cura per assicurare che il vostro server sia sicuro, prima di abiltare mod_dav.

Ogni cartella del server dove DAV è abilitato deve essere protetta da autenticazione. L’uso della HTTP Basic Authentication non è consigliata. Occorre usare almeno la HTTP Digest Authentication, che è fornita dal modulo di Apache mod_auth_digest. Quasi tutti i client WebDAV supportano questo metodo di autenticazione.

Una alternativa è la Basic Authentication sopra una connessione SSL/TLS.

Per consentire al modulo mod_dav la gestione dei file, lo User e il Group proprietari del processo UNIX che esegue Apache (nel sistema operativo Ubuntu è l’utente www-data) devono essere abilitati alla scrittura delle directory e file al disotto di queste.  I nuovi file creati apparterranno all’utente User:Group. Per tale ragione è importante controllare l’accesso di questo account. Il repository DAV è considerato privato di Apache. Pertanto, la modifica di file al di fuori di Apache (ad esempio tramite FTP o comandi del sistema operativo) non dovrebbe essere permessa.

PHP 7 – what’s new – III

Dichiarazioni di tipo ritornato da una funzione

Dal PHP7 è possibile specificare il tipo di ritorno di una funzione, cioè qual è il tipo della variabile ritornata da una funzione. I tipi disponibili sono i quattro riportati nell’articolo precedente (int, string, float, bool).

// 6.php

function sum(array $array): int {
    return array_sum($array);
}

$s = sum([1,2,3]);
echo gettype($s) .": ".$s . "\n";

Output:

$ php 6.php 
integer: 6

A cosa ci serve dichiarare il tipo di ritorno? Per tutti i linguaggi è normale farlo e il compilatore stesso può dare indicazioni se c’è qualche incoerenza di tipo. PHP consente di farlo e il funzionamento può essere utilizzato per imporre un uso di tipi più controllato di quanto un programmatore PHP sia abituato a fare. Ad esempio possiamo definire la funzione div():

// 6.1.php

function div(int $a, $b): int {
    return $a / $b;
}

$s = div(1,2);
echo gettype($s) .": ".$s . "\n";

Output:

$ php 6.1.php 
integer: 0

Ma possiamo anche definire diversamente il tipo di ritorno:

// 6.2.php

function div(int $a, $b): float {
    return $a / $b;
}

$s = div(1,2);
echo gettype($s) .": ".$s . "\n";

nel qual caso ho un diverso comportamento

$ php 6.2.php 
double: 0.5

PHP7 – what’s new – II

Classe TypeError

Dalla versione 7 di PHP, come visto nell’articolo precedente, abbiamo a disposizione una nuova classe che permette di gestire gli errori di tipo; in un esempio come quello sopra possiamo gestire in autonomia l’errore:

declare(strict_types=1);

// Coercive mode
function sumOfInts(int ...$ints)
{
    //echo gettype($ints)."\n";
    return array_sum($ints);
}

try {
    var_dump(sumOfInts(2, '3', 4.1));
} catch (TypeError $e) {
    echo 'Errore di tipo: '.$e->getMessage()."\n";
}        

Output:

Errore di tipo: Argument 3 passed to sumOfInts() must be of the type integer, float given, 
called in /var/www/html/php7/2.php on line 13

PHP7 – what’s new – I

Tipizzazione degli scalari

Facciamo una piccola introduzione. PHP è un linguaggio debolmente tipizzato, il che lo rende inviso a molti coders. Esso consente di fare cose strane del tipo

$a = '0';
$b = $a + 1; <---- questa è la cosa strana
$echo $b;
echo ':';
echo gettype($b)
// Output 1:integer

Ovvero l’operatore + (somma tra numeri interi/virgola mobile) è stato applicato ad una variabile $a che è string (che già è quanto meno esotico) e ad una costante che è integer per cui, a runtime, il compilatore ha deciso che il tipo del risultato $b andava conformato a quello della costante; prima di tutto ha effettuato il cast della stringa a intero ($a è “diventata” = 0, intero, non come stringa) e poi ha sommato la costante 1.

Alla fine il risultato è

// Output 1:integer

Effettivamente se non si fa attenzione a ciò che si scrive potremmo trovarci in situazioni inspiegabili e non abbiamo un compilatore che ci avverte (PHP è un linguaggio compilato ma la compilazione in bytecode viene fatta in modo silente dal motore Zend ogni volta che si lancia il programma dopo averlo modificato).

Nelle chiamate a funzione esiste la possibilità di forzare il tipo degli argomenti o in modo coattivo (coercive) oppure in modo stretto (strict).

Riprendiamo la funzione dell’articolo precedente e modifichiamo la lista dei parametri passati a runtime:

// 1.php - Coercive mode
function sumOfInts(int ...$ints) {
    return array_sum($ints);
}

var_dump(sumOfInts(2, '3', 4.1));

Possiamo specificare il tipo (un unico tipo per tutti i parametri) premettendolo ai tre puntini che precedono il nome dell’array; il tipo può essere SOLO uno dei seguenti:

  • int
  • string
  • float
  • bool

L’output di questo programma è

$ php 1.php 
/var/www/html/php7/1.php:10:
int(9)    

Nota che tutte le componenti dell’array sono state forzate a int, per cui 2+3+4=9. Questo comportamento è assunto per default, ovvero: in mancanza di indicazioni precise gli argomenti vengono forzati al tipo indicato.

Se si vuole che il blocco di codice invece utilizzi strettamente i tipi indicati, occorre istruire PHP attraverso una dichiarazione declare():

// Strict mode

declare(strict_types=1);

function sumOfInts(int ...$ints) {
    return array_sum($ints);
}

var_dump(sumOfInts(2, '3', 4.1));

l’output in questo caso evidenzia errori che riguardano solo l’errato tipo delle variabili in posizione 2 e 3:

PHP Fatal error:  Uncaught TypeError: Argument 2 passed to sumOfInts() must be of the type integer, string given, 
called in /var/www/html/php7/1.php on line 12 and defined in /var/www/html/php7/1.php:6
Stack trace:
#0 /var/www/html/php7/1.php(12): sumOfInts(2, '3', 4.1)
#1 {main}

Next TypeError: Argument 3 passed to sumOfInts() must be of the type integer, float given, 
called in /var/www/html/php7/1.php on line 12 and defined in /var/www/html/php7/1.php:6
Stack trace:
#0 /var/www/html/php7/1.php(12): sumOfInts(2, '3', 4.1)
#1 {main}
  thrown in /var/www/html/php7/1.php on line 6 

Se faccio girare questo script con una versione di PHP < 7 ho invece questo warning che riguarda la dichiarazione strict_types che è propria solo di PHP7:

[Tue Sep 25 11:43:12.440261 2018] [:error] [pid 4263] [client 127.0.0.1:52730] PHP Warning:  Unsupported declare 'strict_types' 
in /var/www/html/php7/1.php on line 3, referer: http://js/php7/

Tuttavia PHP 5.6 riconosce comunque l’impropria assegnazione di variabili non int a variabili dichiarate int e quindi fallisce:

[Tue Sep 25 11:43:12.441202 2018] [:error] [pid 4263] [client 127.0.0.1:52730] PHP Catchable fatal error:  Argument 1 
passed to sumOfInts() must be an instance of int, integer given, called in /var/www/html

Come si può vedere, c’è un sacco di roba che si può imparare dagli errori.

Diciamo senza malizia che l’effetto di PHP 7 in questo caso è di ammorbidire un già debole controllo sui tipi… Infatti se non imponiamo strict_types=1 lui fa una conversione al volo e fa funzionare un pezzo di codice che in PHP 5.6 non funzionerebbe! Tuttavia, se confrontiamo gli output di errore tra PHP 7 e PHP 5.6, vediamo che non si tratta più di un Catchable fatal error, ma di un errore di un nuovo tipo: TypeError (che vedremo nella prossima pillola).

PHP7 – what’s new

Oggi inizio a pubblicare delle pillole di PHP che riguardano le nuove caratteristiche introdotte nel PHP7. Seguono quelle enunciate nel sito php.net ma le commento e aggiungo qualche esempio.

Comincio con 

Passaggio di un numero indefinito di argomenti ad una funzione

Questo argomento non rapresenta una novità introdotta in PHP 7 ma è in qualche modo propedeutico ad altre novità. Generalmente una funzione ha un numero di argomenti definito in sede di dichiarazione. Ad esempio

// 3.php

function sum($a, $b) {
    return $a + $b;
}

echo sum(1, 2, 3); 

Qui però in sede di invocazione della funzione, passiamo più parametri di quelli dichiarati. Ciò che accade è che la somma venga eseguita solo sui primi due argomenti ma non c’è alcun errore sull’invocazione della API della funzione. Questo è quanto accade sia in PHP 7 che nelle versioni precedenti

$ php 3.php 
3

Come si vede, il programma ha sommato solo i primi due argomenti (1 e 2) e non il terzo (3). Questo comportamento è molto diverso da quello di altri linguaggi. Nella stessa situazione, ad esempio, un programma Python genera un errore:

#!/usr/bin/python
# 3.py

def sum(a,b):
   return a+b
   
print sum(1,2,3)

Output:

$ ./3.py 
Traceback (most recent call last):
  File "./3.py", line 6, in <module>
    print sum(1,2,3)
TypeError: sum() takes exactly 2 arguments (3 given)

Dunque PHP non da’ errore se la lista degli argomenti passati alla funzione differisce in numero da quella indicata nella dichiarazione, semplicemente ignora i parametri che in numero eccedono la definizione.

Dalla versione 5.6 tuttavia è stato introdotto un particolare operatore “” (detto “… token” nel sito di PHP) che consente di rendere dinamico il numero degli argomenti passati alla funzione che possono essere contenuti in un array di dimensione decisa a runtime.

// 4.php
function sumOfInts(int ...$ints) {
    return array_sum($ints);
}

var_dump(sumOfInts(1, 2));
var_dump(sumOfInts(2, 3, 4));

che fornisce l’output

$ php 4.php 
/var/www/html/php7/4.php:7:
int(3)
/var/www/html/php7/4.php:8:
int(9)

Pillole Linux: ancora grep

Eccoci ad un’altra puntata sul (potentissimo) programma grep GNU Regular Expression Parser – Analizzatore di Espressioni Regolari GNU (le altre puntate le trovate qui e qui).

L’output atteso di grep durante una ricerca può presentarci qualche sorpresa nel caso interpreti il file come binario

Se grep interpreta il file come binario e non come testo, anziché scrivere sullo standard output le linee contenenti le occorrenze del pattern cercato, scriverà un più laconico:

$ grep needle filename
Il file binario filename corrisponde

Però il file può effettivamente contenere del testo analizzabile, per cui possiamo forzare questo comportamento (quello di stampare le occorrenze della stringa needle) aggiungendo una opzione:

$ grep -a needle filename
 12:01:16.360		[VxmlLog.L1]         		LOG: logXML: message:  \volume=20   How do I find a needle inside a haystack?  \pause=0?

Come si vede, l’output di grep è quello atteso: la riga contenente la stringa cercata. L’opzione -a ha il significato di indicare a grep di trattare il file binario come se fosse un file di testo, come si legge dalla man page:

$ man grep
 -a, --text
        Process a binary file as if it were text; this is  
        equivalent to the --binary-files=text option.

Rimane la domanda: perché grep interpreta come binario un file che è evidentemente un file di testo (è un log di una applicazione)?

Il file in oggetto è codificato con la codifica ISO-8859-1 quindi non è un file tecnicamente di puro testo, visto che è stata operata la codifica detta per rappresentare caratteri oltre il settimo bit:

$ file filename
filename: ISO-8859 text, with very long lines, with CRLF line terminators

In ultima analisi è il programma stesso che stabilisce cosa considerare come testo o come non-testo. Infatti la direttiva POSIX è abbastanza generosa a riguardo:

A file that contains characters organized into zero or more lines. The lines do not contain NUL characters and none can exceed {LINE_MAX} bytes in length, including the <newline> character. Although POSIX.1-2017 does not distinguish between text files and binary files (see the ISO C standard), many utilities only produce predictable or meaningful output when operating on text files. The standard utilities that have such restrictions always specify "text files" in their STDIN or INPUT FILES sections

Anche se POSIX.1-2017 non distingue tra file di testo e file binari, molte utilities [come ad esempio grep] producono solamente un output attendibile o sensato quando operano su file di testo. Le utilities standard che hanno questo tipo di restrizione specificano sempre “file di testo” nel loro sezioni STIDIN o INPUT FILES.