I have Laravel setup so Monolog logs to syslog. Here is whats' in my start/global.php:
$monolog = Log::getMonolog();
$monolog->pushHandler(new Monolog\Handler\SyslogHandler('mywebsitename'));
This works well, except all log messages are dumped into /var/log/messages.
How can I specify a specific log file instead of messages? I've seen something called custom "channels" with Monolog. Is it something to do with that?
Also, how do I make sure that this log file is rotated like the rest of my log files?
I'm using Laravel 4.2, and in my setLog function I've added the RotatingFileHandler. This will solve both problems for you.
This code creates a file called mttParser-{Y-m-d}.log in app/storage/logs/import/, where {Y-m-d} is converted to todays date. The second parameter to the RotatingFileHandler, 10, sets to keep 10 versions of the log file before deleting them.
The IntrospectionProcessor simply adds the method, class, line number, where the log was called.
trait LogTrait
{
/**
* #var string The location within the storage path to put the log files
*/
protected $logFileLocation = 'logs/import/';
protected $logFileName = 'mttParser.log';
/**
* #var Logger
*/
protected $log;
/**
* Returns a valid log file, re-using an existing one if possible
*
* #return \Monolog\Logger
*/
public function getLog()
{
// Set a log file
if (!isset( $this->log )) {
$this->setLog();
}
// If it's been set somewhere else, re-create it
if ('Monolog\Logger' !== get_class( $this->log )) {
$this->setLog();
}
return $this->log;
}
/**
* Sets the log file
*/
public function setLog()
{
$this->log = new Logger( 'mttParserLog' );
// Make sure our directory exists
if (!File::isDirectory( storage_path( $this->logFileLocation ) )) {
File::makeDirectory( storage_path( $this->logFileLocation ) );
}
$this->log->pushHandler( new RotatingFileHandler( storage_path( $this->logFileLocation . $this->logFileName ),
10 ) );
$this->log->pushProcessor( new IntrospectionProcessor() );
}
}
Related
In Hadoop program, I tried to compress the result, I wrote the following code:
FileOutputFormat.setCompressOutput(job, true);
FileOutputFormat.setOutputCompressorClass(job, GzipCodec.class);
The result was compressed, and when I delete the first line:
FileOutputFormat.setCompressOutput(job, true);
and execute the program again, the result was same, was the above code
FileOutputFormat.setCompressOutput(job, true);
optional? What is the function of that code?
Please see the below methods in FileOutPutFormat.java which internally calls the method call which you have deleted.
i.e setCompressOutput(conf, true);
That means you are trying apply Gzip codec class then obviously its a pointer to code that output should be compressed. Isnt it ?
/**
* Set whether the output of the job is compressed.
* #param conf the {#link JobConf} to modify
* #param compress should the output of the job be compressed?
*/
public static void setCompressOutput(JobConf conf, boolean compress) {
conf.setBoolean("mapred.output.compress", compress);
}
/**
* Set the {#link CompressionCodec} to be used to compress job outputs.
* #param conf the {#link JobConf} to modify
* #param codecClass the {#link CompressionCodec} to be used to
* compress the job outputs
*/
public static void
setOutputCompressorClass(JobConf conf,
Class<? extends CompressionCodec> codecClass) {
setCompressOutput(conf, true);
conf.setClass("mapred.output.compression.codec", codecClass,
CompressionCodec.class);
}
I'm using the Gradle application plugin for my Java app.
The start scripts Gradle generates work fine but I'd prefer if the console wouldn't pop up when the user starts the application.
Is there a way to do this?
By modifying the start script I got what I wanted (just for Windows for now).
build.gradle:
apply from: "IO.gradle"
// Modify the Windows start script so that no console is shown when the user starts the app.
// This also creates a copy of the original start script in case we want to use the console for debugging
startScripts << {
def startScriptDir = outputDir.getAbsolutePath()
def winStartScript = startScriptDir + "/" + applicationName + ".bat"
def winStartScriptCopy = startScriptDir + "/" + applicationName + "WithConsole.bat"
def overwriteExistingFile = true
copyFile(winStartScript, winStartScriptCopy, overwriteExistingFile)
modifyFile(winStartScript) {
// javaw.exe doesn't have a console
if(it.contains("java.exe")){
return it.replace("java.exe", "javaw.exe")
}
// Command that launches the app
else if(it.startsWith("\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS%")){
return "start \"\" /b " + it
}
// Leave the line unchanged
else{
return it
}
}
}
installApp {
// Include the additional start script
into("bin/"){
from(startScripts.outputDir)
}
}
IO.gradle:
import java.nio.*
import java.nio.file.*
/**
* This will completely re-write a file, be careful.
*
* Simple Usage:
*
* modifyFile("C:\whatever\whatever.txt") {
* if(it.contains("soil"))
* return null // remove dirty word
* else
* return it
* }
*
* The closure must return the line passed in to keep it in the file or alter it, any alteration
* will be written in its place.
*
* To delete an entire line instead of changing it, return null
* To add more lines after a given line return: it + "\n" + moreLines
*
* Notice that you add "\n" before your additional lines and not after the last
* one because this method will normally add one for you.
*/
def modifyFile(srcFile, Closure c) {
modifyFile(srcFile, srcFile, c)
}
def modifyFile(srcFile, destFile, Closure c={println it;return it}) {
StringBuffer ret = new StringBuffer();
File src = new File(srcFile)
File dest = new File(destFile)
src.withReader{reader->
reader.eachLine{
def line=c(it)
if(line != null) {
ret.append(line)
ret.append("\n")
}
}
}
dest.delete()
dest.write(ret.toString())
}
/**
* Copies a file specified at 'origin' to 'destination'.
* If 'overwrite' is set to true any existing file at 'destination' is overwritten (defaults to false).
*/
def copyFile(String origin, String destination, boolean overwrite=false){
Path origPath = Paths.get(origin)
Path destPath = Paths.get(destination)
def fileAtDestination = destPath.toFile()
if(fileAtDestination.exists()){
if(overwrite) {
fileAtDestination.delete()
Files.copy(origPath, destPath)
}
else{
println("Won't overwrite existing file $fileAtDestination")
println("Call 'copyFile(orig, dest, true)' to delete the existing file first")
}
}
else {
// There's no file at the destination yet
Files.copy(origPath, destPath)
}
}
// Define methods visible to other Gradle scripts
ext{
modifyFile = this.&modifyFile
copyFile = this.©File
}
modifyFile is authored by Bill K.
This is an old post, but I stumbled across it with the same problem.
The modifications to the start script in the answer by Matthias Braun are good, however I think it's cleaner to do it in the following way:
Modify the default template for windows with the modifications pointed out (use javaw.exe and modify the startup command to silence the console).
Then modify the startScript template instead of modifying the generated scripts in place: This can be done as shown in How do I change unixStartScriptGenerator.template in the createStartScripts task so that distTar uses my custom template file in build.gradle?:
startScripts {
def tplName = 'windowsStartScriptWithoutConsoleTemplate.txt'
assert project.file(tplName).exists()
unixStartScriptGenerator.template = resources.text.fromFile(tplName)
}
Clearly, this does not also add a second startScript with the console present, but for me that is not necessary.
I have a Auth.Attempt event handler class, which I detect user's login attempts to decide to lock user's account.
However, when I tried to redirect user to login page with a flash message, I found the redirection does not work, it's still carry on next step.
I want to interrupt the process in the event and give my custom warning message. Can anyone help me out? Thanks a lot.
My event handler:
namespace MyApp\Handlers\Security;
use DB;
use Session;
use Redirect;
class LoginHandler
{
/**
* Maximum attempts
* If user tries to login but failed more than this number, User account will be locked
*
* #var integer
*/
private $max_attemtps;
/**
* Maximum attempts per IP
* If an IP / Device tries to login but failed more than this number, the IP will be blocked
*
* #var integer
*/
private $ip_max_attempts;
public function __construct()
{
$this->max_attempts = 10;
$this->ip_max_attempts = 5;
}
public function onLoginAttempt($data)
{
//detection process.......
// if login attempts more than max attempts
return Redirect::to('/')->with('message', 'Your account has been locked.');
}
}
Now the way I am doing this is like below:
Session::flash('message', 'Your account has been locked.');
header('Location: '.URL::to('/'));
It works but I am not sure if it's perfect way to do it.
You can still send an HttpException who will work. But obviously instructions after the event handler will not be interpreted
abort(redirect('/'));
Not getting to much into this very interesting discussion:
Should exceptions be used for flow control
You can try setting up your own exception handler and redirect from there on to the login page.
class FancyException extends Exception {}
App::error(function(FancyException $e, $code, $fromConsole)
{
$msg = $e->getMessage();
Log::error($msg);
if ( $fromConsole )
{
return 'Error '.$code.': '.$msg."\n";
}
if (Config::get('app.debug') == false) {
return Redirect::route('your.login.route');
}
else
{
//some debug stuff here
}
});
And in your function:
public function onLoginAttempt($data)
{
//detection process.......
// if login attempts more than max attempts
throw new FancyException("some msg here");
}
I am constructing a tree using the Google Closure Library. Now I want the nodes to expand on a single mouseclick, but I seem not to get it working.
I've tried calling goog.ui.component.EventType.SELECT, but it won't work.
At my tree-component class I've added the following event:
goog.events.listen(item, [goog.ui.Component.EventType.SELECT, goog.ui.tree.BaseNode.EventType.EXPAND], this.dispatchEvent, false, this);
And at my class calling the function i've added:
goog.events.listen(this._tree, [goog.ui.Component.EventType.SELECT, goog.ui.tree.BaseNode.EventType.EXPAND], this.treeClick, false, this)
Any suggestions on how I could expand my node with a single click?
I see that BaseNode is screwing up any click events:
/**
* Handles a click event.
* #param {!goog.events.BrowserEvent} e The browser event.
* #protected
* #suppress {underscore}
*/
goog.ui.tree.BaseNode.prototype.onClick_ = goog.events.Event.preventDefault;
When adding this code to the goog/demos/tree/demo.html:
goog.ui.tree.BaseNode.prototype.onClick_ = function (e) {
var qq = this.expanded_?this.collapse():this.expand();
};
The tree control expands and collapses on one click.
Tried to extend goog.ui.tree.TreeControl and override createNode to return a custom goog.ui.tree.TreeNode that overrides onClick but could not do it without getting errors. In theory you could create your custom TreeControl that expands and collapses on one click.
If you want to support non collapsable content and other features I guess you have to trigger some sort of event instead of just extend or callapse the TreeNode.
[update]
After some fiddling I got it working by subclassing TreeControl and TreeNode added the following code to goog/demos/tree/demo.html
/**
* This creates a myTreeControl object. A tree control provides a way to
* view a hierarchical set of data.
* #param {string} html The HTML content of the node label.
* #param {Object=} opt_config The configuration for the tree. See
* goog.ui.tree.TreeControl.defaultConfig. If not specified, a default config
* will be used.
* #param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
* #constructor
* #extends {goog.ui.tree.BaseNode}
*/
var myTreeControl = function (html, opt_config, opt_domHelper) {
goog.ui.tree.TreeControl.call(this, html, opt_config, opt_domHelper);
};
goog.inherits(myTreeControl, goog.ui.tree.TreeControl);
/**
* Creates a new myTreeNode using the same config as the root.
* myTreeNode responds on single clicks
* #param {string} html The html content of the node label.
* #return {goog.ui.tree.TreeNode} The new item.
* #override
*/
myTreeControl.prototype.createNode = function (html) {
return new myTreeNode(html || '', this.getConfig(),
this.getDomHelper());
};
/**
* A single node in the myTreeControl.
* #param {string} html The html content of the node label.
* #param {Object=} opt_config The configuration for the tree. See
* goog.ui.tree.TreeControl.defaultConfig. If not specified, a default config
* will be used.
* #param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
* #constructor
* #extends {goog.ui.tree.BaseNode}
*/
var myTreeNode = function (html, opt_config, opt_domHelper) {
goog.ui.tree.TreeNode.call(this,html, opt_config, opt_domHelper)
}
goog.inherits(myTreeNode, goog.ui.tree.TreeNode);
/**
* Handles a click event.
* #param {!goog.events.BrowserEvent} e The browser event.
* #override
*/
myTreeNode.prototype.onClick_ = function (e) {
goog.base(this, "onClick_", e);
this.onDoubleClick_(e);
};
Changed the creation of the tree variable:
tree = new myTreeControl('root', treeConfig);
Works on single clicks and have not noticed any other things breaking. I've added the annotations so it'll compile easier. You might put the declarations in a separate file with a goog.provide.
Too bad the handleMouseEvent_ of TreeControl is private or you'll just override that one but you can't without changing either TreeControl source or getting compile errors/warnings. Ach wel, jammer.
I received the following error:
Call to undefined method CI_Loader::plugin() in C:\wamp\www\Code\application\libraries\DX_Auth.php on line 1233
on this code:
function captcha()
{
$this->ci->load->helper('url');
$this->ci->load->plugin('dx_captcha');
$captcha_dir = trim($this->ci->config->item('DX_captcha_path'), './');
Make sure you have moved any array values in application/config/autoload.php from $autoload[‘plugins’] to $autoload[‘helpers’] or you will notice stuff break.
This is the reference
Which version of CI are you using? Plugins has been removed since the 2.x and replaced with helper.
Try to use reCaptcha instead, it has a good library.
You're trying to load a plugin and plugins are not supported, if I remember it right since CI version 2. If that's the case (which seems to be) you need to convert your plugins into helpers.
I think you are trying to use an old version of DX_Auth on new version of CodeIgniter. Current version of DX_Auth is compatible with CI 2.x and is available on github.
A simple way to solve this problem is that you just put this code in your loader.php. The plugin its works. Go to System->Core->Loader.php.
/**
* Load Plugin
*
* This function loads the specified plugin.
*
* #access public
* #param array
* #return void
*/
function plugin($plugins = array())
{
if ( ! is_array($plugins))
{
$plugins = array($plugins);
}
foreach ($plugins as $plugin)
{
$plugin = strtolower(str_replace(EXT, '', str_replace('_pi', '', $plugin)).'_pi');
if (isset($this->_ci_plugins[$plugin]))
{
continue;
}
if (file_exists(APPPATH.'plugins/'.$plugin.EXT))
{
include_once(APPPATH.'plugins/'.$plugin.EXT);
}
else
{
if (file_exists(BASEPATH.'plugins/'.$plugin.EXT))
{
include_once(BASEPATH.'plugins/'.$plugin.EXT);
}
else
{
show_error('Unable to load the requested file: plugins/'.$plugin.EXT);
}
}
$this->_ci_plugins[$plugin] = TRUE;
log_message('debug', 'Plugin loaded: '.$plugin);
}
}
// --------------------------------------------------------------------
/**
* Load Plugins
*
* This is simply an alias to the above function in case the
* user has written the plural form of this function.
*
* #access public
* #param array
* #return void
*/
function plugins($plugins = array())
{
$this->plugin($plugins);
}