APP_SERVER env var not found when deploying with EasyDeploy - php

I have this error when the deploy script tries to install the assets:
(Setup PHP 7.2, Symfony 4.4)
[Symfony\Component\Process\Exception\ProcessFailedException]
The command "ssh -A root#vps.net -p 21 '(export APP_ENV=prod; cd /var/www-deploy/site.com/releases/20191129224025
&& php /var/www-deploy/site.com/releases/20191129224025/bin/console assets:install /var/www-deploy/site.com/releases/20191129224025/public --symlink --no-debug --env=prod)'" failed.
Exit Code: 1(General error)
Working directory: /Users/coil/Sites/site.com
Output:
================
Error Output:
================
In EnvVarProcessor.php line 162:
Environment variable not found: "APP_SERVER".
I have put the .env file in the shared files, so it's copied when the release directory is created, this file is in the directory.
public function configure(): DefaultConfiguration
{
return $this->getConfigBuilder()
// ...
->sharedFilesAndDirs(['.env'])
// ...
}
I also tried to put the env vars in the .bashrc file of the user who deploys:
printenv | grep -i app
APP_DEBUG=0
APP_SECRET=whatever
APP_ENV=prod
APP_SERVER=prod
So, I don't understand, why the environment variables are still not found. And why they are not taken from the .env file.
If I interrupt the deployment and if I manually run the same command on the server it works well. Thanks.
[Edit 2019-11-30] I think I understand know, when running the command, there is an export of the APP_ENV that is done. And, if the application detects this env variable it will just skip the .env file. So, I need to avoid setting this env var, or I must set all instead of this one only.

[Edit 2021-02-13] I have fixed the bin/console file and removed the "hack". Instead of testing the APP_ENV var I test the APP_SECRET one which is not set by EasyDebug:
if (!isset($_SERVER['APP_SECRET'])) {
(new Dotenv())->load(__DIR__.'/../.env');
}
Old solution:
It now works with the following modififications, in src/Configuration/DefaultConfiguration.php:
} elseif (4 === $symfonyMajorVersion || (3 === $symfonyMajorVersion && 4 >= $symfonyMinorVersion)) {
$this->_symfonyEnvironmentEnvVarName = 'APP_ENV';
I have passed an unkown env var to Symfony:
$this->_symfonyEnvironmentEnvVarName = 'APP_FAKE_ENV';
So the .env file is not ignored any-more. I think there is a missing option to set or not this environment var based on the setup of the application. I'll try to do a PR to the bundle.
So, as the bundle is not modified yet, I have modified the private property, in order to make the command use my .env files instead of environment variables:
/**
* #see https://stackoverflow.com/q/1762135/633864
*/
private function forceEnvironmentEnvVarName(DefaultConfiguration $configuration): DefaultConfiguration
{
$myClassReflection = new ReflectionClass(get_class($configuration));
$symfonyEnvironmentEnvVarName = $myClassReflection->getProperty('_symfonyEnvironmentEnvVarName');
$symfonyEnvironmentEnvVarName->setAccessible(true);
$symfonyEnvironmentEnvVarName->setValue($configuration, 'FAKE_APP_ENV');
$symfonyEnvironmentEnvVarName->setAccessible(false);
return $configuration;
}

Related

AWS Lambda with php.handler node 12.x error - SOLVED

UPDATE: Thank you all! I have solved this by creating a custom runtime for my PHP Lambda.
I am currently using Node.js 8.10 Runtime with a php.handler and my Lambda function works fine, but when I change the Runtime to 12.x, I get the following error:
"php-7-bin/bin/php: error while loading shared libraries: libcrypt.so.1: cannot open shared object file: No such file or directory"
exports.handler = function(event, context, callback) {
var php = spawn('php-7-bin/bin/php',['--php-ini', 'user.ini', process.env['PHPFILE']], {maxBuffer: 200 * 1024 * 200});
var output = "";
var statusCode = 0;
php.stdin.write(JSON.stringify(event));
php.stdin.end();
php.stdout.on('data', function(data) {
console.log("CHUNK: " + data);
output+=data;
});
php.stderr.on('data', function(data) {
console.log(data);
});
php.on('close', function(code) {
var obj = JSON.parse(output);
statusCode = obj.status.statusCode;
if(statusCode !== 0){
callback(output);
}else{
context.succeed(obj);
}
});
}
I need to update my Lambda to the latest node.js version, but I have no idea how to overcome this error, so any help would be greatly appreciated!
First, why on earth are you using node to load php?
But if you had this working before, why do you need to update to node 12?
If you are upgrading from Node 8, the runtime is different:
https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html
So then take a look here:
https://aws.amazon.com/blogs/apn/aws-lambda-custom-runtime-for-php-a-practical-example/
You may need to create a new custom runtime based off the node12 built-in runtime for AWS.
Easy fix is to add on top of your PHP code:
set_include_path('/opt/lib64’);
If that won't work you need to compile/build/install the missing modules/libraries by yourself:
Run two docker instances that will have mounted the same “local” Layer folder.
First container is going to be your lambda container while second one is Amazon linux used to build items.
Test your code with Lambda container and in case something is missing switch to Amazon Linux and build/extract binaries/libraries into shared Layer folder structure.
Make sure the Lambda code have proper PATH defined to use Layer folder.
Install docker.
In first terminal tab go to your lambda folder and start the lambda docker container:
docker run --rm -it --entrypoint=/bin/bash -v "$PWD":/var/task:ro,delegated -v /your/path/to/Layer/folder/:/opt:rw,delegated -e AWS_ACCESS_KEY_ID=[ACCESS_KEY_PASTE_HERE -e AWS_SECRET_ACCESS_KEY=[SECRET_GOES_HERE] lambci/lambda:nodejs12.x
In second terminal tab run another container with Amazon linux:
docker run --rm -it -v /your/path/to/Layer/folder/:/opt:rw,delegated amazonlinux:latest
(Keep in mind that the Layer folder is mounted with read/write permissions).
Test your lambda code in your favourite way or just by simple run (make sure to check if your handler module name is “handler” and file name is “index.js”):

cd /var/task
node index.js; node "var func = require('./index.js');func.handler({},function() {},function(){console.log('Lambda finished')});"
In case you find some missing libraries, make sure to add to your PHP code:
set_include_path('/opt/lib');
Then on Amazon Linux terminal tab and install/build your library and then copy it to Layer folder:
cp /usr/lib64/[here is your library name] /opt/lib
Test again your code in Lambda container.
When you will be done just zip the content of your Lambda Layer structure, keep in mind that your \bin ora \lib folders need to be in the root folder of the zip file.
Add the zip file as a Layer for you lambdas and attach it.
I Fix this problem by adding extra library folder in my function's zip.
Make a directory name extra-libs
Copy all required libraries from Amazon Linux 2 to Extra-libs by using following steps :
Run amazon Linux 2's docker instance by following command
docker run --rm -it -v :/opt:rw,delegated amazonlinux:latest
Then in docker instance make directory using
mkdir deps
Copy all required libraries from lib64 to deps directory using
cp -f lib64/libcrypt.so.1 deps (Taken libcrypt.so.1 for example purpose)
Then open another terminal window and move all library files to local extra-libs
docker cp <DOCKER_CONTAINER_ID>:/deps/ . && mv deps/* ./extra-libs
Get container id by using docker ps
Then in index.js file , add following line to php's env setting.
LD_LIBRARY_PATH:path.join(__dirname, '/extra-libs')
Zip extra-libs folder with your lambda function and upload it.
Hope this helps.

how to solve error QXcbConnection: Could not connect to display when using exec function for phantomJs using PHP

I am working on a project where I want to use PHP and Phantomjs together, I have completed my phantomJs script and trying to run it using php exec function. but the function is returning an array of error list.
below I am writing my code of phantomjs and php
dir: /var/www/html/phantom/index.js
var page = require('webpage').create();
var fs = require('fs');
page.open('http://insttaorder.com/', function(status) {
// Get all links to CSS and JS on the page
var links = page.evaluate(function() {
var urls = [];
$("[rel=stylesheet]").each(function(i, css) {
urls.push(css.href);
});
$("script").each(function(i, js) {
if (js.src) {
urls.push(js.src);
}
});
return urls;
});
// Save all links to a file
var url_file = "list.txt";
fs.write(url_file, links.join("\n"), 'w');
// Launch wget program to download all files from the list.txt to current
// folder
require("child_process").execFile("wget", [ "-i", url_file ], null,
function(err, stdout, stderr) {
console.log("execFileSTDOUT:", stdout);
console.log("execFileSTDERR:", stderr);
// After wget finished exit PhantomJS
phantom.exit();
});
});
dir: /var/www/html/phantom/index.php
exec('/usr/bin/phantomjs index.js 2>&1',$output);
echo '<pre>';
print_r($output);
die;
Also tried with
exec('/usr/bin/phantomjs /var/www/html/phantom/index.js 2>&1',$output);
echo '<pre>';
print_r($output);
die;
After runing this i am getting below error
Array
(
[0] => QXcbConnection: Could not connect to display
[1] => PhantomJS has crashed. Please read the bug reporting guide at
[2] => and file a bug report.
[3] => Aborted (core dumped)
)
But if I run index.php file from the terminal like this:
user2#user2-H81M-S:/var/www/html/phantom$ php index.php
then it works fine.I don't know how to solve it. Please help.
i am using following version
system version: Ubuntu 16.04.2 LTS
PHP version: 5.6
phantomJs version: 2.1.1
Did you tried to set an environment variable on your server ? or added it before calling phantomjs ?
I was in the same situation and found some solutions:
a. define or set variable QT_QPA_PLATFORM to offscreen:
QT_QPA_PLATFORM=offscreen /usr/bin/phantomjs index.js
b. or add this line into your .bashrc file (put it at the end):
export QT_QPA_PLATFORM=offscreen
c. or install the package xvfb and call xvfb-run before phantomjs:
xvfb-run /usr/bin/phantomjs index.js
d. or use the parameter platform:
/usr/bin/phantomjs -platform offscreen index.js
Maybe you don't want / can't make modification on your server and in that case you may try to download the static binary from official website then:
/path/to/the/bin/folder/phantomjs index.js
and / or create an alias in your .bash_aliases file like this:
alias phantomjs=/path/to/the/bin/folder/phantomjs
make sure that phantomjs is not installed already on the system if you decide to use the alias.
if the file .bash_aliases not exist already, feel free to create it or add the alias line at the end of the .bashrc file
Some references:
https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=817277
https://github.com/ariya/phantomjs/issues/14376
https://bugs.launchpad.net/ubuntu/+source/phantomjs/+bug/1586134
I had the same problem running phantomjs on headless Ubuntu 18.04 (on the default Vagrant vm install of openstreetmap-website). Folloiwng Jiab77's links, it seems the Phantomjs team says the problem is the Debian package but the Debian team closed the bug as wontfix. I needed phantomjs to "just work" so it can be called by other programs that expect it to work normally. Specifically, openstreetmap-website has an extensive Ruby test suite with over 40 tests that were failing because of this, and I didn't want to modify all those tests.
Following Jiab77's answer, here's how I made it work:
As root, cp /usr/bin/phantomjs /usr/local/bin/phantomjs
Edit /usr/local/bin/phantomjs and add the line export QT_QPA_PLATFORM=offscreen so it runs before execution. Here is what mine says after doing so:
#!/bin/sh
LD_LIBRARY_PATH="/usr/lib/phantomjs:$LD_LIBRARY_PATH"
export LD_LIBRARY_PATH
# 2018-11-13: added the next line so phantomjs can run headless as explained on
# https://stackoverflow.com/questions/49154209/how-to-solve-error-qxcbconnection-could-not-connect-to-display-when-using-exec
export QT_QPA_PLATFORM=offscreen
exec "/usr/lib/phantomjs/phantomjs" "$#"
After this change, phantomjs can be run from the command line without changing anything else, and all the tests that depend on phantomjs were successfully passed.

How to tell Deployer to use different PHP version once ssh'ed to my shared hosting?

I'm experimenting with Deployer to deploy Laravel application into shared hosting (using laravel recipe) from my local ~/Code/project_foo.
The point is that when I'm connected to my shared hosting server via ssh, then default php -v version is 5.6.33. I confirmed that I can change php version on fly by calling php70 -v or even whole path like /usr/local/bin/php70 whatever.
The point is that I don't know how to tell deployer to call commands using php70 which is required, otherwise composer install fails.
So in Terminal I'm inside root of the Laravel project and I simply call:
dep deploy
My deploy.php is messy and very simple but this is just a proof of concept. I'm trying to figure out everything and then I will make it look nicer.
I checked the source code of the laravel recipe, and I saw that there is:
{{bin/php}}
but I don't know how to override the value to match what my hosting tells me to use:
/usr/local/bin/php70
Please, give me any hints how to force the script use different PHP version once connected to the remote host / server.
This is whole script:
<?php
namespace Deployer;
require 'recipe/laravel.php';
//env('bin/php', '/usr/local/bin/php70'); // <- I thought that this will work but it doesn't change anything
// Project name
set('application', 'my_project');
// Project repository
set('repository', 'git#github.com:xxx/xxx.git');
// [Optional] Allocate tty for git clone. Default value is false.
set('git_tty', true);
// Shared files/dirs between deploys
add('shared_files', []);
add('shared_dirs', []);
// Writable dirs by web server
add('writable_dirs', []);
// Hosts
host('xxx')
->user('xxx')
->set('deploy_path', '/home/slickpl/projects/xxx');
// Tasks
task('build', function () {
run('cd {{release_path}} && build');
});
// [Optional] if deploy fails automatically unlock.
after('deploy:failed', 'deploy:unlock');
// Migrate database before symlink new release.
before('deploy:symlink', 'artisan:migrate');
OK, I've found the solution.
I added (after require):
set('bin/php', function () {
return '/usr/local/bin/php70';
});
For anybody who searches for changing Composer's PHP version:
set('bin/composer', function () {
return '/usr/bin/php7.4 /usr/local/bin/composer';
});
There is function locateBinaryPath()
so result is:
set('bin/php', function () {
return locateBinaryPath('php7.4');
});
first found php path and composer path use this
for more information Setting PHP versions in Deployer deployments
find / -type f -name "php" 2>&1 | grep -v "Permission denied"
find / -type f -name "composer" 2>&1 | grep -v "Permission denied"
then
set('bin/composer',
function () {
return 'php_path composer_path';
});
like this
set('bin/composer',
function () {
return '/opt/remi/php73/root/usr/bin/php /usr/bin/composer';
});

Lumen Micro Framework => php artisan key:generate

I'm trying out the PHP micro Framework Lumen (from Laravel).
One of my first steps was to look into the .env.example file and make a copy of it to have my .env file. There is a variable APP_KEY just like there is in Laravel. Now I tried out the simple command php artisan key:generate to get my new key But I ran into the following error message:
[InvalidArgumentException] There are no commands defined in the "key"
namespace.
Does some one know how I can generate keys for Lumen?
Update with solution
So I found my favorite solution for this problem. On the command line (Linux) I run php -r "echo md5(uniqid()).\"\n\";" what gives me something like this 7142720170cef01171fd4af26ef17c93.
If you are going to use Lumen more often, you may want to create an alias in your .bashrc, which is located in your home directory /home/USERNAME. To do so, you can open the file with nano ~/.bashrc or vi ~/.bashrc and copy the following alias at the end of the file, alias phpkey='php -r "echo md5(uniqid()).\"\n\";"'. Now you can use the command phpkey which will give you a 32 character long random string :)
The Laravel command is fairly simple. It just generates a random 32 character long string. You can do the same in Lumen. Just temporarily add a route like this:
$router->get('/key', function() {
return \Illuminate\Support\Str::random(32);
});
Then go to /key in your browser and copy paste the key into your .env file.
Afterwards remove the route.
Obviously you could also use some random string generator online. Like this one
Firstly, you have to register your key generator command, put this Lumen Key Generator Commands to app/Console/Commands/KeyGenerateCommand.php. To make this command available in artisan, change app\Console\Kernel.php:
/**
* The Artisan commands provided by your application.
*
* #var array
*/
protected $commands = [
'App\Console\Commands\KeyGenerateCommand',
];
After that, configure your application so that Illuminate\Config\Repository instance has app.key value. To do this, change bootstrap/app.php:
<?php
require_once __DIR__.'/../vendor/autoload.php';
Dotenv::load(__DIR__.'/../');
/*
|--------------------------------------------------------------------------
| Create The Application
|--------------------------------------------------------------------------
|
| Here we will load the environment and create the application instance
| that serves as the central piece of this framework. We'll use this
| application as an "IoC" container and router for this framework.
|
*/
$app = new Laravel\Lumen\Application(
realpath(__DIR__.'/../')
);
$app->configure('app');
After that, copy your .env.example file to .env:
cp .env.example .env
Ignore this step if you already use .env file.
Enjoy you key:generate command via:
php artisan key:generate
Edit
You may use Lumen Generator. It covers so much commands you are missing from Laravel.
Simply use PHP CLI. Run this from your local or a remote command line to generate a random 32-character Lumen APP_KEY:
php -r "echo bin2hex(random_bytes(16));"
Output: bae48aba23b3e4395b7f1b484dd25192
Works with PHP 7.x on Mac and Windows.
An easy solution is just running PHP code from the terminal (without using tinker, because that is not available with Lumen):
php -r "require 'vendor/autoload.php'; echo str_random(32).PHP_EOL;"
It uses Laravel's Str::random() function that makes use of the secure random_bytes() function.
For me the easiest way to generate a Lumen key is typing on console one of these commands:
date | md5
date | md5sum
or
openssl rand -base64 24
depending of your environment. In my case, I aways use date | md5 on mac
The APP_KEY generation is a step of development process (I don't think that creating temporarily routes is a practical way to do it). The function str_random can help us, but this function is part of Laravel/Lunmen framework.
I recommend running tinker
php artisan tinker
and then run the function
>>> str_random(32)
The result is the key you're looking for.
=> "y3DLxnEczGWGN4CKUdk1S5GbMumU2dfH"
To generate key and use laravel command you need to install one package. The details are as below:
You have to install package composer require flipbox/lumen-generator
You have to add $app->register(Flipbox\LumenGenerator\LumenGeneratorServiceProvider::class); into bootstrap/app.php file.
Link: https://github.com/flipboxstudio/lumen-generator
All I do on mac is execute this command in the terminal:
date | md5 | pbcopy
This copies the value into the clipboard and so you can easily paste the key into the .env file.
I have used these commands:
php -r \"copy('.env.example', '.env');\"
php -r "echo password_hash(uniqid(), PASSWORD_BCRYPT).\"\n\";"
The command generates a key similar to this:
$2y$10$jb3kw/vUANyzZ4ncMa4rwuR09qldQ2OjX8PGrVB5dIlSnUAPCGjFe
This answer was inspired by #thomas-venturini 's update to the question. Here's a bash script that takes care of creating .env and updating it with an APP_KEY using the aforementioned PHP command and the UNIX sed command:
#!/usr/bin/env bash
function generate_app_key {
php -r "echo md5(uniqid()).\"\n\";"
}
APP_KEY=$(generate_app_key)
sed -e s/APP_KEY=.*$/APP_KEY=${APP_KEY}/g .env.example > .env
Hope someone finds this useful.
Run php -a to start up interactive php playground.
Then run echo substr(md5(rand()), 0, 32); to generate a 32 character string.
You can then copy/paste into the .env file.
1.Open your terminal setup file:
vim ~/.zshrc
2.Create an alias for generating random strings:
# Lumen Key Generate
alias lumen-key="php -r \"require 'vendor/autoload.php'; echo base64_encode(str_random(32)).PHP_EOL;\""
3.Get a key whenever you need:
~/your-lumen-project via 🐘 v7.3.0
➜ lumen-key
VkxTYWZZSnhVNVEzRThXelBGZVJDVGZVYTNTcm9peHY=
You can also remove the third step by adding the key directly in .env using PHP.
[Flipbox\LumenGenerator]
Fix error: there are no comands defined...
[boostrap/app] Check if you register the Flipbox\LumenGenerator after return $app. If so move the Service provider register before return app...
/**
* Configure extra LARAVEL commands to a lumen app
* Check avaliable commands in git: flipboxstudio lumen-generator
*/
if($app->environment() !== 'production'){
$app->register(Flipbox\LumenGenerator\LumenGeneratorServiceProvider::class);
}
return $app;
Lumen 8.0 / flipbox/lumen-generator 8.2
It Worked 100%
Simply install the flipbox/lumen-generator package
composer require flipbox/lumen-generator.
Inside your bootstrap/app.php file, add:
$app->register(Flipbox\LumenGenerator\LumenGeneratorServiceProvider::class);
Then after you can able to run php artisan commands,
more info: https://github.com/flipboxstudio/lumen-generator

Environment variable not recognised when deploying to Elastic Beanstalk

I'm trying to deploy my Laravel application to Elastic Beanstalk in development mode. To make the application run in development mode rather than production, I've done the following in my /bootstrap/start.php file:
$env = $app->detectEnvironment(function() {
return $_ENV['ENV_NAME'];
});
To actually create the environment variable, I've created a .config file in the following path: /.ebextensions/00environmentVariables.config with these contents:
option_settings:
- namespace: aws:elasticbeanstalk:application:environment
option_name: ENV_NAME
value: development
- option_name: DB_HOST
value: [redacted]
- option_name: DB_PORT
value: [redacted]
- option_name: DB_NAME
value: [redacted]
- option_name: DB_USER
value: [redacted]
- option_name: DB_PASS
value: [redacted]
When I run eb start from the command line, it spins up an EC2 instance and attempts to provision it, at which point it tells me it fails. and to check the logs. In the logs, I can see these entries:
PHP Notice: Undefined index: ENV_NAME in
/var/app/ondeck/bootstrap/start.php on line 28
Notice: Undefined index: ENV_NAME in /var/app/ondeck/bootstrap/start.php on line 28
So for some reason, the ENV_NAME environment variable doesn't exist, even though I've specified it in 00environmentVariables.config. What's even weirder, is that I can see the environment variable does exist under the software configuration settings of the EB environment:
To summarize:
I know my .config files are being parsed from looking at the logs
For some reason my Laravel application still doesn't think that ENV_NAME eixsts
ENV_NAME eixsts both in the .config file and in my Elastic Beanstalk settings for this environment
EDIT
Alright so I worked out that the environment variables do work correctly when serving the application over the Apache HTTP server, but the environment variables don't exist when running the PHP CLI.
In the above logs, it's complaining about ENV_NAME not existing when running a /usr/bin/composer.phar install.
So, for some reason, my environment variables don't exist within the PHP CLI but they do work normally when serving over Apache.
FURTHER EDIT
So I SSH'd into the EC2 instance that's hosting my Laravel application on Elastic Beanstalk, and I can see the proper environment variables when I use the ``printenv command`:
ENV_NAME=development
However, if I do a die(var_dump($_SERVER)); and run the PHP CLI, I don't see the environment variables that I've defined. Same story with $_ENV and getenv().
Why can't I access my environment variables within the PHP CLI, when I can access them when Apache processes my PHP scripts?
YET ANOTHER EDIT
I made a test.php file with one line: die(var_dump($_ENV));.
When I run this using php test.php I successfully get my custom environment variables coming out, so this seems like a composer only problem, not a PHP CLI problem.
I use a YAML script which sets the environment variables for the root user from the existing variables set for ec2-user. Add this to your .ebextensions folder with the .config extension.
From there you can run PHP cli and it will see the correct environment variables
commands:
create_post_dir:
command: "mkdir /opt/elasticbeanstalk/hooks/appdeploy/post"
ignoreErrors: true
files:
"/opt/elasticbeanstalk/hooks/appdeploy/post/job_after_deploy.sh":
mode: "000755"
owner: root
group: root
content: |
#!/usr/bin/env bash
source /opt/elasticbeanstalk/support/envvars
# Run PHP scripts here. #
From XuDing's answer to this question and this answer
I created a job that creates .env file every 5 minutes.
Add the following to your .ebextensions
"/opt/elasticbeanstalk/hooks/appdeploy/post/91_set_create_app_env_file_job.sh":
mode: "000755"
owner: root
group: root
content: |
#!/usr/bin/env bash
echo "Removing any existing CRON jobs..."
crontab -r
APP_ENV=/var/app/current/.env
EB_ENVVARS=/opt/elasticbeanstalk/support/envvars
CONSTANTS=/var/app/current/.constants
CRON_CMD="grep -oE '[^ ]+$' $EB_ENVVARS > $APP_ENV; cat $CONSTANTS >> $APP_ENV"
echo "Creating .env file...."
eval $CRON_CMD
echo "Scheduling .env file updater job to run every 5 minutes..."
(crontab -l 2>/dev/null; echo "*/5 * * * * $CRON_CMD")| crontab -
Reason I did it this way is that you may want to update your environment variables via the AWS UI Console.
This is the best solution in my opinion.

Categories